blob: d4043ad97b8701b59c5a6be9b29ba1550d8d0f9a [file] [log] [blame]
/*
* linux/drivers/video/pxa168fb.c -- Marvell PXA168 LCD Controller
*
* Copyright (C) 2008 Marvell International Ltd.
* All rights reserved.
*
* 2009-02-16 adapted from original version for PXA168
* Kevin Liu <kliu5@marvell.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/console.h>
#include <linux/slab.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_PXA688_PHY
#include "pxa168fb.h"
#include <mach/io.h>
#include <mach/irqs.h>
#include <mach/pxa168fb.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <asm/mach-types.h>
#include <mach/regs-apmu.h>
#include <mach/mfp-mmp2.h>
#include <mach/regs-mpmu.h>
#include <asm/mach-types.h>
#include "pxa168fb_common.h"
/* dsi phy timing */
static struct dsi_phy phy = {
.hs_prep_constant = 40, /* Unit: ns. */
.hs_prep_ui = 4,
.hs_zero_constant = 145,
.hs_zero_ui = 10,
.hs_trail_constant = 0,
.hs_trail_ui = 64,
.hs_exit_constant = 100,
.hs_exit_ui = 0,
.ck_zero_constant = 300,
.ck_zero_ui = 0,
.ck_trail_constant = 60,
.ck_trail_ui = 0,
.req_ready = 0x3c,
.wakeup_constant = 1000000,
.wakeup_ui = 0,
.lpx_constant = 50,
.lpx_ui = 0,
};
#define dsi_ex_pixel_cnt 0
#define dsi_hex_en 0
/* (Unit: Mhz) */
#define dsi_hsclk (clk_get_rate(fbi->clk)/1000000)
#define dsi_lpclk 3
#define to_dsi_bcnt(timing, bpp) (((timing) * (bpp)) >> 3)
static unsigned int dsi_lane[5] = {0, 0x1, 0x3, 0x7, 0xf};
static int is_odd_1s(u32 data)
{
int num = 0;
while (data) {
if (data % 2)
num++;
data = data >> 1;
}
if (num % 2)
return 1;
return 0;
}
static unsigned char caluate_ecc(u32 data)
{
int ecc_bit_depend[6], i;
u32 tmp;
unsigned char ret = 0;
ecc_bit_depend[0] = 0xf12cb7;
ecc_bit_depend[1] = 0xf2555b;
ecc_bit_depend[2] = 0x749a6d;
ecc_bit_depend[3] = 0xb8e38e;
ecc_bit_depend[4] = 0xdf03f0;
ecc_bit_depend[5] = 0xeffc00;
for (i = 0; i < 6; i++) {
tmp = data & ecc_bit_depend[i];
ret |= is_odd_1s(tmp) ? (1 << i) : 0;
}
return ret;
}
/* FIXME: only for short packet */
void dsi_send_cmd(struct pxa168fb_info *fbi,
enum dsi_packet_di data_type, enum dsi_packet_dcs_id dcs, u8 parameter)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
u32 send_data, waddr, tmp, ecc, count;
send_data = data_type & 0xff;
send_data |= (dcs & 0xff) << 8;
send_data |= (parameter & 0xff) << 16;
ecc = caluate_ecc(send_data);
send_data |= (ecc & 0xff) << 24;
writel(send_data, &dsi->dat0);
waddr = 0xc0000000;
writel(waddr, &dsi->cmd3);
count = 1000;
while (readl(&dsi->cmd3) & 0x80000000 && count)
count--;
if (count <= 0)
pr_err("%s error!\n", __func__);
tmp = 0xC8000000 | 4;
writel(tmp, &dsi->cmd0);
pr_debug("send data:%x, cmd3:%x, cmd0:%x\n",
send_data, waddr, tmp);
/* wait for completion */
count = 1000;
while (readl(&dsi->cmd0) & 0x80000000 && count)
count--;
if (count <= 0)
pr_err("%s error!\n", __func__);
}
void pxa168fb_dsi_send(struct pxa168fb_info *fbi, void *value)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
u8 *dsi_cmd_tmp = (u8 *)value;
int count = (int)(*dsi_cmd_tmp);
u8 *dsi_cmd = (u8 *)&dsi_cmd_tmp[1];
unsigned int firstTimeFlag = 1, firstPacket = 0;
volatile int loop = 0, tmp = 0, bc = 0, dsiAddr = 0, wAddr = 0;
pr_debug("count is %d\r\n", count);
/* write all packet bytes to packet data buffer */
for (loop = 0; loop < count; loop++) {
tmp |= ((int)dsi_cmd[loop]) << (bc * 8);
bc++;
pr_debug("bc is %d\r\n", bc);
if (bc == 4) {
/* XM: save 1st packet */
if (firstTimeFlag) {
firstTimeFlag = 0;
firstPacket = tmp;
}
writel(tmp, &dsi->dat0);
wAddr = 0xC0000000 | (dsiAddr << 16);
writel(wAddr, &dsi->cmd3);
/* while (readl(&dsi->cmd3) & 0x80000000)
msleep(1); */
pr_debug("total count is %d, wAddr is 0x%08x,"
" data is 0x%08x\r\n", count, wAddr, tmp);
tmp = 0; bc = 0; dsiAddr += 4;
}
}
/* handle last none 4Byte align data */
if (bc) {
writel(tmp, &dsi->dat0);
wAddr = 0xC0000000 | (dsiAddr << 16);
writel(wAddr, &dsi->cmd3);
/* while (readl(&dsi->cmd3) & 0x80000000)
msleep(1); */
pr_debug("last one total count is %d, wAddr is 0x%08x,"
" data is 0x%08x\r\n", count, wAddr, tmp);
tmp = 0;
}
/* send out the packet */
tmp = 0xC8000000 | count; writel(tmp, &dsi->cmd0);
pr_debug("write count is 0x%08x\r\n", tmp);
/* wait for completion */
while (readl(&dsi->cmd0) & 0x80000000)
msleep(1);
}
#if 0 /* original version */
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
int loop = 0, tmp = 0;
u8 *dsi_cmd = (u8 *)value;
int count = (int)(*dsi_cmd);
if (count != 0x4) {
/* set up packet header for long packet */
*(dsi_cmd + 1) = 0x29;
*(dsi_cmd + 2) = (u8)(count - 6);
*(dsi_cmd + 3) = 0;
}
/* write all packet bytes to packet data buffer */
for (loop = 0; loop < count; loop++) {
tmp |= ((int)*(dsi_cmd + loop + 1)) << ((loop % 4) * 8);
if (!((loop + 1) % 4)) {
writel(tmp, &dsi->dat0);
writel(0xc0000000 | ((loop - 3) << 16), &dsi->cmd3);
while (readl(&dsi->cmd3) & 0x80000000)
msleep(1);
tmp = 0;
}
}
if (loop % 4) {
writel(tmp, &dsi->dat0);
writel(0xc0000000 | (4 * (loop / 4) << 16), &dsi->cmd3);
while (readl(&dsi->cmd3) & 0x80000000)
msleep(1);
tmp = 0;
}
/* send out the packet */
if (count == 0x4)
tmp = 0xc0000000 | count;
else
tmp = 0x80000000 | (count - 6);
writel(tmp, &dsi->cmd0);
while (readl(&dsi->cmd0) & 0x80000000)
msleep(1);
}
#endif
void dsi_cclk_set(struct pxa168fb_info *fbi, int en)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
if (en)
writel(0x1, &dsi->phy_ctrl1);
else
writel(0x0, &dsi->phy_ctrl1);
}
void dsi_lanes_enable(struct pxa168fb_info *fbi, int en)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
u32 reg = readl(&dsi->phy_ctrl2) & ~(0xf << 4);
reg &= ~(0xf << 4);
if (en)
reg |= (dsi_lane[di->lanes] << 4);
pr_debug("%s %d: phy_ctrl2 0x%x\n", __func__, en, reg);
writel(reg, &dsi->phy_ctrl2);
}
void dsi_set_dphy(struct pxa168fb_info *fbi)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
int ui, lpx_clk, lpx_time, ta_get, ta_go, wakeup, reg;
int hs_prep, hs_zero, hs_trail, hs_exit, ck_zero, ck_trail, ck_exit;
ui = 1000/dsi_hsclk + 1;
pr_debug("ui:%d\n", ui);
lpx_clk = (phy.lpx_constant + phy.lpx_ui * ui) / DSI_ESC_CLK_T;
lpx_time = (lpx_clk + 1) * DSI_ESC_CLK_T;
pr_debug("lpx_clk:%d, condition (TIME_LPX:%d > 50)\n",
lpx_clk, lpx_time);
/* Below is for NT35451 */
ta_get = lpx_time * 5 / DSI_ESC_CLK_T - 1;
ta_go = lpx_time * 4 / DSI_ESC_CLK_T - 1;
pr_debug("ta_get:%d, condition (TIME_TA_GET:%d == 5*TIME_LPX:%d)\n",
ta_get, (ta_get + 1) * DSI_ESC_CLK_T, lpx_time * 5);
pr_debug("ta_go:%d, condition (TIME_TA_GO:%d == 4*TIME_LPX:%d)\n",
ta_go, (ta_go + 1) * DSI_ESC_CLK_T, lpx_time * 4);
wakeup = phy.wakeup_constant;
wakeup = wakeup / DSI_ESC_CLK_T + 1;
pr_debug("wakeup:%d, condition (WAKEUP:%d > MIN:%d)\n",
wakeup, (wakeup + 1) * DSI_ESC_CLK_T, 1000000);
hs_prep = phy.hs_prep_constant + phy.hs_prep_ui * ui;
hs_prep = hs_prep / DSI_ESC_CLK_T + 1;
pr_debug("hs_prep:%d, condition (HS_PREP_MAX:%d > HS_PREP:%d "
"> HS_PREP_MIN:%d)\n", hs_prep, 85 + 6 * ui,
(hs_prep + 1) * DSI_ESC_CLK_T, 40 + 4 * ui);
/* Our hardware added 3-byte clk automatically.
* 3-byte 3 * 8 * ui.
*/
hs_zero = phy.hs_zero_constant + phy.hs_zero_ui * ui -
(hs_prep + 1) * DSI_ESC_CLK_T;
hs_zero = (hs_zero - (3 * ui << 3)) / DSI_ESC_CLK_T + 4;
if (hs_zero < 0)
hs_zero = 0;
pr_debug("hs_zero:%d, condition (HS_ZERO + HS_PREP:%d > SUM_MIN:%d)\n",
hs_zero, (hs_zero - 2) * DSI_ESC_CLK_T + 24 * ui +
(hs_prep + 1) * DSI_ESC_CLK_T, 145 + 10 * ui);
hs_trail = phy.hs_trail_constant + phy.hs_trail_ui * ui;
hs_trail = hs_trail / DSI_ESC_CLK_T + 1;
pr_debug("hs_trail:%d, condition (HS_TRAIL:%d > MIN1:%d / MIN2:%d "
"/ MIN3:%d)\n", hs_trail, (hs_trail + 1) * DSI_ESC_CLK_T,
8 * ui, 60 + 4 * ui, 64 * ui);
hs_exit = phy.hs_exit_constant + phy.hs_exit_ui * ui;
hs_exit = hs_exit / DSI_ESC_CLK_T + 1;
pr_debug("hs_exit:%d, condition (HS_EXIT:%d > MIN:%d)\n",
hs_exit, (hs_exit + 1) * DSI_ESC_CLK_T, 100);
ck_zero = phy.ck_zero_constant + phy.ck_zero_ui * ui -
(hs_prep + 1) * DSI_ESC_CLK_T;
ck_zero = ck_zero / DSI_ESC_CLK_T + 1;
pr_debug("ck_zero:%d, condition (CK_ZERO + CK_PREP:%d > SUM_MIN:%d)\n",
ck_zero, (ck_zero + 1) * DSI_ESC_CLK_T +
(hs_prep + 1) * DSI_ESC_CLK_T, 300);
ck_trail = phy.ck_trail_constant + phy.ck_trail_ui * ui;
ck_trail = ck_trail / DSI_ESC_CLK_T + 1;
pr_debug("ck_trail:%d, condition (CK_TRIAL:%d > MIN:%d)\n",
ck_trail, (ck_trail + 1) * DSI_ESC_CLK_T, 60);
ck_exit = hs_exit;
pr_debug("ck_exit:%d\n", ck_exit);
/* bandgap ref enable */
reg = readl(&dsi->phy_rcomp0);
reg |= (1 << 9);
writel(reg, &dsi->phy_rcomp0);
/* timing_0 */
reg = (hs_exit << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_EXIT_SHIFT)
| (hs_trail << DSI_PHY_TIME_0_CFG_CSR_TIME_HS_TRAIL_SHIFT)
| (hs_zero << DSI_PHY_TIME_0_CDG_CSR_TIME_HS_ZERO_SHIFT)
| (hs_prep);
writel(reg, &dsi->phy_timing0);
reg = (ta_get << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GET_SHIFT)
| (ta_go << DSI_PHY_TIME_1_CFG_CSR_TIME_TA_GO_SHIFT)
| wakeup;
writel(reg, &dsi->phy_timing1);
reg = (ck_exit << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_EXIT_SHIFT)
| (ck_trail << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_TRAIL_SHIFT)
| (ck_zero << DSI_PHY_TIME_2_CFG_CSR_TIME_CK_ZERO_SHIFT)
| lpx_clk;
writel(reg, &dsi->phy_timing2);
reg = (lpx_clk << DSI_PHY_TIME_3_CFG_CSR_TIME_LPX_SHIFT) | \
phy.req_ready;
writel(reg, &dsi->phy_timing3);
/* calculated timing on brownstone:
* DSI_PHY_TIME_0 0x06080204
* DSI_PHY_TIME_1 0x6d2bfff0
* DSI_PHY_TIME_2 0x603130a
* DSI_PHY_TIME_3 0xa3c
*/
}
void dsi_reset(struct pxa168fb_info *fbi, int hold)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
volatile unsigned int reg;
printk(KERN_DEBUG "%s\n", __func__);
writel(0x0, &dsi->ctrl0);
reg = readl(&dsi->ctrl0);
reg |= DSI_CTRL_0_CFG_SOFT_RST | DSI_CTRL_0_CFG_SOFT_RST_REG;
if (!hold) {
writel(reg, &dsi->ctrl0);
reg &= ~(DSI_CTRL_0_CFG_SOFT_RST | DSI_CTRL_0_CFG_SOFT_RST_REG);
mdelay(1);
}
writel(reg, &dsi->ctrl0);
}
void dsi_set_controller(struct pxa168fb_info *fbi)
{
struct fb_var_screeninfo *var = &(fbi->fb_info->var);
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
struct dsi_lcd_regs *dsi_lcd = &dsi->lcd1;
unsigned hsync_b, hbp_b, hact_b, hex_b, hfp_b, httl_b;
unsigned hsync, hbp, hact, hfp, httl, h_total, v_total;
unsigned hsa_wc, hbp_wc, hact_wc, hex_wc, hfp_wc, hlp_wc;
int bpp = di->bpp, hss_bcnt = 4, hse_bct = 4, lgp_over_head = 6, reg;
if (di->id & 2)
dsi_lcd = &dsi->lcd2;
pr_debug("%s dsi %d lanes %d burst_mode %d bpp %d\n",
__func__, di->id, di->lanes, di->burst_mode, bpp);
h_total = var->xres + var->left_margin +
var->right_margin + var->hsync_len;
v_total = var->yres + var->upper_margin +
var->lower_margin + var->vsync_len;
hact_b = to_dsi_bcnt(var->xres, bpp);
hfp_b = to_dsi_bcnt(var->right_margin, bpp);
hbp_b = to_dsi_bcnt(var->left_margin, bpp);
hsync_b = to_dsi_bcnt(var->hsync_len, bpp);
hex_b = to_dsi_bcnt(dsi_ex_pixel_cnt, bpp);
httl_b = hact_b + hsync_b + hfp_b + hbp_b + hex_b;
hact = hact_b / di->lanes;
hfp = hfp_b / di->lanes;
hbp = hbp_b / di->lanes;
hsync = hsync_b / di->lanes;
httl = hact + hfp + hbp + hsync;
/* word count in the unit of byte */
hsa_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? \
(hsync_b - hss_bcnt - lgp_over_head) : 0;
/* Hse is with backporch */
hbp_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? \
(hbp_b - hse_bct - lgp_over_head) \
: (hsync_b + hbp_b - hss_bcnt - lgp_over_head);
hfp_wc = ((di->burst_mode == DSI_BURST_MODE_BURST) && \
(dsi_hex_en == 0)) ? \
(hfp_b + hex_b - lgp_over_head - lgp_over_head) : \
(hfp_b - lgp_over_head - lgp_over_head);
hact_wc = ((var->xres) * bpp) >> 3;
/* disable Hex currently */
hex_wc = 0;
/* There is no hlp with active data segment. */
hlp_wc = (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? \
(httl_b - hsync_b - hse_bct - lgp_over_head) : \
(httl_b - hss_bcnt - lgp_over_head);
/* FIXME - need to double check the (*3) is bytes_per_pixel from
* input data or output to panel */
/* dsi_lane_enable - Set according to specified DSI lane count */
writel(dsi_lane[di->lanes] << DSI_PHY_CTRL_2_CFG_CSR_LANE_EN_SHIFT,
&dsi->phy_ctrl2);
writel(dsi_lane[di->lanes] << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT,
&dsi->cmd1);
/* SET UP LCD1 TIMING REGISTERS FOR DSI BUS */
/* NOTE: Some register values were obtained by trial and error */
writel((hact << 16) | httl, &dsi_lcd->timing0);
writel((hsync << 16) | hbp, &dsi_lcd->timing1);
/*
* For now the active size is set really low (we'll use 10) to allow
* the hardware to attain V Sync. Once the DSI bus is up and running,
* the final value will be put in place for the active size (this is
* done below). In a later stepping of the processor this workaround
* will not be required.
*/
writel(((var->yres)<<16) | (v_total), &dsi_lcd->timing2);
writel(((var->vsync_len) << 16) | (var->upper_margin),
&dsi_lcd->timing3);
/* SET UP LCD1 WORD COUNT REGISTERS FOR DSI BUS */
/* Set up for word(byte) count register 0 */
writel((hbp_wc << 16) | hsa_wc, &dsi_lcd->wc0);
writel((hfp_wc << 16) | hact_wc, &dsi_lcd->wc1);
writel((hex_wc << 16) | hlp_wc, &dsi_lcd->wc2);
/* calculated value on brownstone:
* WC0: 0x1a0000
* WC1: 0x1500f00
* WC2: 0x1076 */
/* Configure LCD control register 1 FOR DSI BUS */
reg = ((di->rgb_mode << DSI_LCD2_CTRL_1_CFG_L1_RGB_TYPE_SHIFT)
| (di->burst_mode << DSI_LCD1_CTRL_1_CFG_L1_BURST_MODE_SHIFT)
| (di->lpm_line_en ? DSI_LCD1_CTRL_1_CFG_L1_LPM_LINE_EN : 0)
| (di->lpm_frame_en ? DSI_LCD1_CTRL_1_CFG_L1_LPM_FRAME_EN : 0)
| (di->last_line_turn ? DSI_LCD1_CTRL_1_CFG_L1_LAST_LINE_TURN : 0)
| (di->hex_slot_en ? 0 : 0) /* disable Hex slot */
| (di->all_slot_en ? 0 : 0) /* disable all slots */
| (di->hbp_en ? DSI_LCD1_CTRL_1_CFG_L1_HBP_PKT_EN : 0)
| (di->hact_en ? DSI_LCD1_CTRL_1_CFG_L1_HACT_PKT_EN : 0)
| (di->hfp_en ? DSI_LCD1_CTRL_1_CFG_L1_HFP_PKT_EN : 0)
| (di->hex_en ? 0 : 0) /* Hex packet is disabled */
| (di->hlp_en ? DSI_LCD1_CTRL_1_CFG_L1_HLP_PKT_EN : 0));
reg |= (di->burst_mode == DSI_BURST_MODE_SYNC_PULSE) ? \
(((di->hsa_en) ? DSI_LCD1_CTRL_1_CFG_L1_HSA_PKT_EN : 0)
| (DSI_LCD1_CTRL_1_CFG_L1_HSE_PKT_EN)) /* Hse is eabled */
:
(((di->hsa_en) ? 0 : 0) /* Hsa packet is disabled */
| ((di->hse_en) ? 0 : 0)); /* Hse packet is disabled */
reg |= DSI_LCD1_CTRL_1_CFG_L1_VSYNC_RST_EN;
writel(reg, &dsi_lcd->ctrl1);
/*Start the transfer of LCD data over the DSI bus*/
/* DSI_CTRL_1 */
reg = readl(&dsi->ctrl1);
reg &= ~(DSI_CTRL_1_CFG_LCD2_VCH_NO_MASK |
DSI_CTRL_1_CFG_LCD1_VCH_NO_MASK);
reg |= 0x1 << ((di->id & 1) ? DSI_CTRL_1_CFG_LCD2_VCH_NO_SHIFT :
DSI_CTRL_1_CFG_LCD1_VCH_NO_SHIFT);
reg &= ~(DSI_CTRL_1_CFG_EOTP);
if (di->eotp_en)
reg |= DSI_CTRL_1_CFG_EOTP; /* EOTP */
writel(reg, &dsi->ctrl1);
/* DSI_CTRL_0 */
reg = (di->master_mode ? 0 : DSI_CTRL_0_CFG_LCD1_SLV) |
DSI_CTRL_0_CFG_LCD1_TX_EN | DSI_CTRL_0_CFG_LCD1_EN;
if (di->id & 2)
reg = reg << 1;
writel(reg, &dsi->ctrl0);
writel(((var->yres)<<16) | (v_total), &dsi_lcd->timing2);
}
void set_dsi_low_power_mode(struct pxa168fb_info *fbi)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
u32 reg = readl(&dsi->phy_ctrl2);
/* enable data lane data0*/
reg &= ~(0xf << 4);
reg |= (1 << 4);
writel(reg, &dsi->phy_ctrl2);
/* LPDT TX enabled for data0 */
reg = readl(&dsi->cmd1);
reg &= ~(0xf << 20);
reg |= 1 << DSI_CPU_CMD_1_CFG_TXLP_LPDT_SHIFT;
writel(reg, &dsi->cmd1);
}
static int dsi_dump(struct pxa168fb_info *fbi, int f, char *buf, int s)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
int dsi_base = (int)(&dsi->ctrl0);
if (!di) {
pr_err("%s: no dsi info available\n", __func__);
return s;
}
mvdisp_dump(f, "dsi_info: ch %d lanes %d bpp %d\n\n", di->id,
di->lanes, di->bpp);
mvdisp_dump(f, "dsi regs base 0x%p\n", dsi);
mvdisp_dump(f, "\tctrl0 (@%3x):\t0x%x\n",
(int)(&dsi->ctrl0) - dsi_base, readl(&dsi->ctrl0));
mvdisp_dump(f, "\tctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->ctrl1) - dsi_base, readl(&dsi->ctrl1));
mvdisp_dump(f, "\tirq_status (@%3x):\t0x%x\n",
(int)(&dsi->irq_status) - dsi_base, readl(&dsi->irq_status));
mvdisp_dump(f, "\tirq_mask (@%3x):\t0x%x\n",
(int)(&dsi->irq_mask) - dsi_base, readl(&dsi->irq_mask));
mvdisp_dump(f, "\tcmd0 (@%3x):\t0x%x\n",
(int)(&dsi->cmd0) - dsi_base, readl(&dsi->cmd0));
mvdisp_dump(f, "\tcmd1 (@%3x):\t0x%x\n",
(int)(&dsi->cmd1) - dsi_base, readl(&dsi->cmd1));
mvdisp_dump(f, "\tcmd2 (@%3x):\t0x%x\n",
(int)(&dsi->cmd2) - dsi_base, readl(&dsi->cmd2));
mvdisp_dump(f, "\tcmd3 (@%3x):\t0x%x\n",
(int)(&dsi->cmd3) - dsi_base, readl(&dsi->cmd3));
mvdisp_dump(f, "\tdat0 (@%3x):\t0x%x\n",
(int)(&dsi->dat0) - dsi_base, readl(&dsi->dat0));
mvdisp_dump(f, "\tstatus0 (@%3x):\t0x%x\n",
(int)(&dsi->status0) - dsi_base, readl(&dsi->status0));
mvdisp_dump(f, "\tstatus1 (@%3x):\t0x%x\n",
(int)(&dsi->status1) - dsi_base, readl(&dsi->status1));
mvdisp_dump(f, "\tstatus2 (@%3x):\t0x%x\n",
(int)(&dsi->status2) - dsi_base, readl(&dsi->status2));
mvdisp_dump(f, "\tstatus3 (@%3x):\t0x%x\n",
(int)(&dsi->status3) - dsi_base, readl(&dsi->status3));
mvdisp_dump(f, "\tstatus4 (@%3x):\t0x%x\n",
(int)(&dsi->status4) - dsi_base, readl(&dsi->status4));
mvdisp_dump(f, "\tsmt_cmd (@%3x):\t0x%x\n",
(int)(&dsi->smt_cmd) - dsi_base, readl(&dsi->smt_cmd));
mvdisp_dump(f, "\tsmt_ctrl0 (@%3x):\t0x%x\n",
(int)(&dsi->smt_ctrl0) - dsi_base, readl(&dsi->smt_ctrl0));
mvdisp_dump(f, "\tsmt_ctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->smt_ctrl1) - dsi_base, readl(&dsi->smt_ctrl1));
mvdisp_dump(f, "\trx0_status (@%3x):\t0x%x\n",
(int)(&dsi->rx0_status) - dsi_base, readl(&dsi->rx0_status));
mvdisp_dump(f, "\trx0_header (@%3x):\t0x%x\n",
(int)(&dsi->rx0_header) - dsi_base, readl(&dsi->rx0_header));
mvdisp_dump(f, "\trx1_status (@%3x):\t0x%x\n",
(int)(&dsi->rx1_status) - dsi_base, readl(&dsi->rx1_status));
mvdisp_dump(f, "\trx1_header (@%3x):\t0x%x\n",
(int)(&dsi->rx1_header) - dsi_base, readl(&dsi->rx1_header));
mvdisp_dump(f, "\trx_ctrl (@%3x):\t0x%x\n",
(int)(&dsi->rx_ctrl) - dsi_base, readl(&dsi->rx_ctrl));
mvdisp_dump(f, "\trx_ctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->rx_ctrl1) - dsi_base, readl(&dsi->rx_ctrl1));
mvdisp_dump(f, "\trx2_status (@%3x):\t0x%x\n",
(int)(&dsi->rx2_status) - dsi_base, readl(&dsi->rx2_status));
mvdisp_dump(f, "\trx2_header (@%3x):\t0x%x\n",
(int)(&dsi->rx2_header) - dsi_base, readl(&dsi->rx2_header));
mvdisp_dump(f, "\tphy_ctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->phy_ctrl1) - dsi_base, readl(&dsi->phy_ctrl1));
mvdisp_dump(f, "\tphy_ctrl2 (@%3x):\t0x%x\n",
(int)(&dsi->phy_ctrl2) - dsi_base, readl(&dsi->phy_ctrl2));
mvdisp_dump(f, "\tphy_ctrl3 (@%3x):\t0x%x\n",
(int)(&dsi->phy_ctrl3) - dsi_base, readl(&dsi->phy_ctrl3));
mvdisp_dump(f, "\tphy_status0(@%3x):\t0x%x\n",
(int)(&dsi->phy_status0) - dsi_base, readl(&dsi->phy_status0));
mvdisp_dump(f, "\tphy_status1(@%3x):\t0x%x\n",
(int)(&dsi->phy_status1) - dsi_base, readl(&dsi->phy_status1));
mvdisp_dump(f, "\tphy_status2(@%3x):\t0x%x\n",
(int)(&dsi->phy_status2) - dsi_base, readl(&dsi->phy_status2));
mvdisp_dump(f, "\tphy_rcomp0 (@%3x):\t0x%x\n",
(int)(&dsi->phy_rcomp0) - dsi_base, readl(&dsi->phy_rcomp0));
mvdisp_dump(f, "\tphy_timing0(@%3x):\t0x%x\n",
(int)(&dsi->phy_timing0) - dsi_base, readl(&dsi->phy_timing0));
mvdisp_dump(f, "\tphy_timing1(@%3x):\t0x%x\n",
(int)(&dsi->phy_timing1) - dsi_base, readl(&dsi->phy_timing1));
mvdisp_dump(f, "\tphy_timing2(@%3x):\t0x%x\n",
(int)(&dsi->phy_timing2) - dsi_base, readl(&dsi->phy_timing2));
mvdisp_dump(f, "\tphy_timing3(@%3x):\t0x%x\n",
(int)(&dsi->phy_timing3) - dsi_base, readl(&dsi->phy_timing3));
mvdisp_dump(f, "\tphy_code_0 (@%3x):\t0x%x\n",
(int)(&dsi->phy_code_0) - dsi_base, readl(&dsi->phy_code_0));
mvdisp_dump(f, "\tphy_code_1 (@%3x):\t0x%x\n",
(int)(&dsi->phy_code_1) - dsi_base, readl(&dsi->phy_code_1));
mvdisp_dump(f, "\tmem_ctrl (@%3x):\t0x%x\n",
(int)(&dsi->mem_ctrl) - dsi_base, readl(&dsi->mem_ctrl));
mvdisp_dump(f, "\ttx_timer (@%3x):\t0x%x\n",
(int)(&dsi->tx_timer) - dsi_base, readl(&dsi->tx_timer));
mvdisp_dump(f, "\trx_timer (@%3x):\t0x%x\n",
(int)(&dsi->rx_timer) - dsi_base, readl(&dsi->rx_timer));
mvdisp_dump(f, "\tturn_timer (@%3x):\t0x%x\n",
(int)(&dsi->turn_timer) - dsi_base, readl(&dsi->turn_timer));
mvdisp_dump(f, "\nlcd1 regs\n");
mvdisp_dump(f, "\tctrl0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.ctrl0) - dsi_base, readl(&dsi->lcd1.ctrl0));
mvdisp_dump(f, "\tctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.ctrl1) - dsi_base, readl(&dsi->lcd1.ctrl1));
mvdisp_dump(f, "\ttiming0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.timing0) - dsi_base,
readl(&dsi->lcd1.timing0));
mvdisp_dump(f, "\ttiming1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.timing1) - dsi_base,
readl(&dsi->lcd1.timing1));
mvdisp_dump(f, "\ttiming2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.timing2) - dsi_base,
readl(&dsi->lcd1.timing2));
mvdisp_dump(f, "\ttiming3 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.timing3) - dsi_base,
readl(&dsi->lcd1.timing3));
mvdisp_dump(f, "\twc0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.wc0) - dsi_base, readl(&dsi->lcd1.wc0));
mvdisp_dump(f, "\twc1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.wc1) - dsi_base, readl(&dsi->lcd1.wc1));
mvdisp_dump(f, "\twc2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.wc2) - dsi_base, readl(&dsi->lcd1.wc2));
mvdisp_dump(f, "\tslot_cnt0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.slot_cnt0) - dsi_base,
readl(&dsi->lcd1.slot_cnt0));
mvdisp_dump(f, "\tslot_cnt1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.slot_cnt1) - dsi_base,
readl(&dsi->lcd1.slot_cnt1));
mvdisp_dump(f, "\tstatus_0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.status_0) - dsi_base,
readl(&dsi->lcd1.status_0));
mvdisp_dump(f, "\tstatus_1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.status_1) - dsi_base,
readl(&dsi->lcd1.status_1));
mvdisp_dump(f, "\tstatus_2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.status_2) - dsi_base,
readl(&dsi->lcd1.status_2));
mvdisp_dump(f, "\tstatus_3 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.status_3) - dsi_base,
readl(&dsi->lcd1.status_3));
mvdisp_dump(f, "\tstatus_4 (@%3x):\t0x%x\n",
(int)(&dsi->lcd1.status_4) - dsi_base,
readl(&dsi->lcd1.status_4));
mvdisp_dump(f, "\nlcd2 regs\n");
mvdisp_dump(f, "\tctrl0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.ctrl0) - dsi_base,
readl(&dsi->lcd2.ctrl0));
mvdisp_dump(f, "\tctrl1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.ctrl1) - dsi_base,
readl(&dsi->lcd2.ctrl1));
mvdisp_dump(f, "\ttiming0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.timing0) - dsi_base,
readl(&dsi->lcd2.timing0));
mvdisp_dump(f, "\ttiming1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.timing1) - dsi_base,
readl(&dsi->lcd2.timing1));
mvdisp_dump(f, "\ttiming2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.timing2) - dsi_base,
readl(&dsi->lcd2.timing2));
mvdisp_dump(f, "\ttiming3 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.timing3) - dsi_base,
readl(&dsi->lcd2.timing3));
mvdisp_dump(f, "\twc0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.wc0) - dsi_base, readl(&dsi->lcd2.wc0));
mvdisp_dump(f, "\twc1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.wc1) - dsi_base, readl(&dsi->lcd2.wc1));
mvdisp_dump(f, "\twc2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.wc2) - dsi_base, readl(&dsi->lcd2.wc2));
mvdisp_dump(f, "\tslot_cnt0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.slot_cnt0) - dsi_base,
readl(&dsi->lcd2.slot_cnt0));
mvdisp_dump(f, "\tslot_cnt1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.slot_cnt1) - dsi_base,
readl(&dsi->lcd2.slot_cnt1));
mvdisp_dump(f, "\tstatus_0 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.status_0) - dsi_base,
readl(&dsi->lcd2.status_0));
mvdisp_dump(f, "\tstatus_1 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.status_1) - dsi_base,
readl(&dsi->lcd2.status_1));
mvdisp_dump(f, "\tstatus_2 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.status_2) - dsi_base,
readl(&dsi->lcd2.status_2));
mvdisp_dump(f, "\tstatus_3 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.status_3) - dsi_base,
readl(&dsi->lcd2.status_3));
mvdisp_dump(f, "\tstatus_4 (@%3x):\t0x%x\n",
(int)(&dsi->lcd2.status_4) - dsi_base,
readl(&dsi->lcd2.status_4));
mvdisp_dump(f, "\ncommands:\n");
mvdisp_dump(f, " - dump all DSI controller registers\n");
mvdisp_dump(f, "\tcat phy\n");
mvdisp_dump(f, " - dump DSI register @ [offset_hex]\n");
mvdisp_dump(f, "\techo -0x[offset_hex] > phy\n");
mvdisp_dump(f, " - set DSI register @ [offset_hex] with [value_hex]\n");
mvdisp_dump(f, "\techo 0x[value_hex] > phy\n");
return s;
}
/* select LVDS_PHY_CTL_EXTx */
#define lvds_ext_select(ext, tmp, reg) do { \
if ((ext < 0) || (ext > 5)) { \
pr_err("%s ext %d not supported\n", __func__, ext); \
return 0; \
} \
reg = (u32)gfx_info.fbi[0]->reg_base + LVDS_PHY_CTL; \
if (ext) { \
/* select LVDS_PHY_CTL_EXTx */ \
tmp = readl(reg) & (~LVDS_PHY_EXT_MASK); \
writel(tmp | (ext - 1) << LVDS_PHY_EXT_SHIFT, reg); \
/* switch to LVDS_PHY_CTL_EXTx */ \
reg -= LVDS_PHY_CTL; reg += LVDS_PHY_CTL_EXT; \
} \
} while (0)
static u32 lvds_get(int ext)
{
u32 reg, tmp;
lvds_ext_select(ext, tmp, reg);
return readl(reg);
}
static int lvds_set(int ext, u32 mask, u32 val)
{
u32 reg, tmp, tmp2;
lvds_ext_select(ext, tmp, reg);
tmp = tmp2 = readl(reg);
tmp2 &= ~mask; tmp2 |= val;
if (tmp != tmp2)
writel(tmp2, reg);
return 0;
}
static int lvds_dump(struct pxa168fb_info *fbi, int f, char *buf, int s)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct lvds_info *lvds = (struct lvds_info *)mi->phy_info;
u32 reg = (u32)fbi->reg_base + LCD_2ND_BLD_CTL;
char *str;
switch (lvds->src) {
case LVDS_SRC_PN:
str = "PN";
break;
case LVDS_SRC_CMU:
str = "CMU";
break;
case LVDS_SRC_PN2:
str = "PN2";
break;
case LVDS_SRC_TV:
str = "TV";
break;
default:
str = "?";
break;
};
mvdisp_dump(f, "lvds_info: src %s fmt %s\n", str,
(lvds->fmt & LVDS_FMT_18BIT) ? "18bit" : "24bit");
mvdisp_dump(f, "LCD_2ND_BLD_CTL(0x%x): 0x%x\n\n",
reg & 0xfff, readl(reg));
mvdisp_dump(f, "LVDS_PHY_CTL: 0x%x\n", lvds_get(0));
mvdisp_dump(f, " EXT1: 0x%x\n", lvds_get(1));
mvdisp_dump(f, " EXT2: 0x%x\n", lvds_get(2));
mvdisp_dump(f, " EXT3: 0x%x\n", lvds_get(3));
mvdisp_dump(f, " EXT4: 0x%x\n", lvds_get(4));
mvdisp_dump(f, " EXT5: 0x%x\n", lvds_get(5));
return s;
}
int pxa688_lvds_config(struct lvds_info *lvds)
{
u32 reg = (u32)gfx_info.fbi[0]->reg_base + LCD_2ND_BLD_CTL;
u32 val = readl(reg) & ~(LVDS_SRC_MASK | LVDS_FMT_MASK);
val |= (lvds->src << LVDS_SRC_SHIFT) | (lvds->fmt << LVDS_FMT_SHIFT);
writel(val, reg);
return 0;
}
int pxa688_lvds_init(struct pxa168fb_info *fbi)
{
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
struct lvds_info *lvds = (struct lvds_info *)mi->phy_info;
int count = 100000;
u32 mask, val;
/* configure lvds src and fmt */
pxa688_lvds_config(lvds);
/* release LVDS PHY from reset */
lvds_set(0, LVDS_RST, 0);
mdelay(1);
/* disable LVDS channel 0-5 power-down */
lvds_set(0, LVDS_PD_CH_MASK, 0);
/* select LVDS_PCLK instead of REFCLK as LVDS PHY clock */
lvds_set(0, LVDS_CLK_SEL, LVDS_CLK_SEL_LVDS_PCLK);
/* power up IP */
lvds_set(0, LVDS_PU_IVREF, LVDS_PU_IVREF);
/* REFDIV = 0x3, reference clock divider
* FBDIV = 0xa, feedback clock divider
* KVCO = 0x4, 1.7G - 1.9G */
mask = LVDS_REFDIV_MASK | LVDS_FBDIV_MASK | LVDS_REFDIV_MASK
| LVDS_CTUNE_MASK | LVDS_VREG_IVREF_MASK
| LVDS_VDDL_MASK | LVDS_VDDM_MASK;
val = (0x6 << LVDS_REFDIV_SHIFT) | (0x1 << LVDS_FBDIV_SHIFT)
| (0x4 << LVDS_KVCO_SHIFT | (0x2 << LVDS_CTUNE_SHIFT)
| (0x2 << LVDS_VREG_IVREF_SHIFT) | (0x9 << LVDS_VDDL_SHIFT)
| (0x1 << LVDS_VDDM_SHIFT));
lvds_set(3, mask, val);
/* VCO_VRNG = 0x3, LVDS PLL V to I gain control, for KVCO[3:0] = 0x4 */
mask = LVDS_VCO_VRNG_MASK | LVDS_ICP_MASK | LVDS_PI_EN
| LVDS_VCODIV_SEL_SE_MASK | LVDS_INTPI_MASK;
val = (0x3 << LVDS_VCO_VRNG_SHIFT) | (0x1 << LVDS_ICP_SHIFT)
| LVDS_PI_EN | (0xd << LVDS_VCODIV_SEL_SE_SHIFT)
| (0x3 << LVDS_INTPI_SHIFT);
lvds_set(4, mask, val);
/* enable PUPLL/PUTX to power up rest of PLL and TX */
lvds_set(0, LVDS_PU_TX | LVDS_PU_PLL, LVDS_PU_TX | LVDS_PU_PLL);
/* poll on lock bit until LVDS PLL locks */
while (!(lvds_get(0) & LVDS_PLL_LOCK) && count--);
if (count <= 0) {
pr_err("%s failed\n", __func__);
lvds_dump(fbi, DUMP_PRINFO, NULL, 0);
}
/* enable common mode feedback circuit */
mask = LVDS_SELLV_OP9_MASK | LVDS_SELLV_OP7_MASK | LVDS_SELLV_OP6_MASK
| LVDS_SELLV_TXDATA_MASK | LVDS_SELLV_TXCLK_MASK | LVDS_TX_DIF_CM_MASK
| LVDS_TX_DIF_AMP_MASK | LVDS_TX_TERM_EN | LVDS_TX_CMFB_EN;
val = (0x1 << LVDS_SELLV_OP9_SHIFT) | (0x1 << LVDS_SELLV_OP7_SHIFT)
| (0x1 << LVDS_SELLV_OP6_SHIFT) | (0x5 << LVDS_SELLV_TXDATA_SHIFT)
| (0x5 << LVDS_SELLV_TXCLK_SHIFT) | (0x3 << LVDS_TX_DIF_CM_SHIFT)
| (0x8 << LVDS_TX_DIF_AMP_SHIFT) | LVDS_TX_TERM_EN;
lvds_set(2, mask, val);
/* Flip all the N\P pins in order to get correct display,
* the pins might be inverted in the chip */
lvds_set(1, LVDS_POL_SWAP_MASK, 0x3f << LVDS_POL_SWAP_SHIFT);
return 0;
}
static void dsi_store(struct pxa168fb_mach_info *mi,
const char *buf, size_t size)
{
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
struct dsi_regs *dsi = (struct dsi_regs *)di->regs;
static u32 mvdsi_reg;
char vol[30];
u32 addr, tmp;
if (size > 30) {
pr_err("%s size = %d > max 30 chars\n", __func__, size);
return;
}
addr = (u32)&dsi->ctrl0;
if ('-' == buf[0]) {
memcpy(vol, buf + 1, size - 1);
mvdsi_reg = (int)simple_strtoul(vol, NULL, 16);
pr_info("dsi reg @ 0x%x: 0x%x\n", mvdsi_reg,
__raw_readl(addr + mvdsi_reg));
} else if ('0' == buf[0] && 'x' == buf[1]) {
/* set the register value */
tmp = (int)simple_strtoul(buf, NULL, 16);
__raw_writel(tmp, addr + mvdsi_reg);
pr_info("set dsi reg @ 0x%x: 0x%x\n", mvdsi_reg,
__raw_readl(addr + mvdsi_reg));
}
}
ssize_t phy_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct pxa168fb_info *fbi = dev_get_drvdata(dev);
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
int s = 0;
if (!mi)
goto out;
if (mi->phy_type & LVDS)
s += lvds_dump(fbi, DUMP_SPRINTF, buf, s);
if ((mi->phy_type & (DSI | DSI2DPI)))
s += dsi_dump(fbi, DUMP_SPRINTF, buf, s);
out:
return s;
}
ssize_t phy_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct pxa168fb_info *fbi = dev_get_drvdata(dev);
struct pxa168fb_mach_info *mi = fbi->dev->platform_data;
int value;
if (mi->phy_type & (DSI | DSI2DPI))
dsi_store(mi, buf, size);
else
sscanf(buf, "%d", &value);
return size;
}
DEVICE_ATTR(phy, S_IRUGO | S_IWUSR, phy_show, phy_store);
#endif