1 /* drivers/video/sprdfb/lcd_hx8394d_mipi.c
3 * Support for hx8394d mipi LCD device
5 * Copyright (C) 2014 Spreadtrum
6 * Author: Haibing.Yang <haibing.yang@spreadtrum.com>
8 * The panel info is got from raolihua@trulyopto.cn
9 * Panel: 5.0HD(720*1280) LG LTPS
11 * Interface: MIPI 4LANES
12 * Test condition: IOVCC=VCC=2.8V
16 * This software is licensed under the terms of the GNU General Public
17 * License version 2, as published by the Free Software Foundation, and
18 * may be copied, distributed, and modified under those terms.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
26 #include <asm/errno.h>
27 #include <asm/arch/sprd_lcd.h>
28 #include "../sprdfb.h"
30 #define pr_info printf
32 #define pr_debug printf
33 #define u8 unsigned char
34 #define u16 unsigned short
35 #define u32 unsigned int
36 #define usleep_range(time1, time2) udelay(time1)
40 #define MAX_RX_PKT_SIZE 0x37
41 #define PANEL_ID_LENGTH 3
42 #define PANEL_RETURN_ID 0x8394
43 #define PANEL_ACTUAL_ID 0x8394
44 #define PANEL_ID_REG 0x04
45 #define PANEL_ID_READ_CNT 3
46 #define PANEL_ID_OFFSET 0
48 #define LCM_TAG_SHIFT 24
49 #define LCM_DATA_TYPE_SHIFT 16
50 #define LCM_TAG_MASK ((1 << LCM_DATA_TYPE_SHIFT) - 1)
52 #define LCM_TAG_SEND (1 << LCM_TAG_SHIFT)
53 #define LCM_TAG_SLEEP (2 << LCM_TAG_SHIFT)
55 #define LCM_SEND(len) (LCM_TAG_SEND | len)
56 #define LCM_SEND_WITH_TYPE(len, type) \
57 (LCM_TAG_SEND | len | (type << LCM_DATA_TYPE_SHIFT))
58 #define LCM_SLEEP(ms) (LCM_TAG_SLEEP | ms)
65 static struct panel_cmds hx8394d_init_cmds[] = {
68 0xB9, 0xFF, 0x83, 0x94}
76 0xB1, 0x6C, 0x12, 0x12, 0x26, 0x04, 0x11, 0xF1,
77 0x81, 0x3A, 0x54, 0x23, 0x80, 0xC0, 0xD2, 0x58}
79 { /* Set display related register */
81 0xB2, 0x00, 0x64, 0x0E, 0x0D, 0x22, 0x1C, 0x08,
82 0x08, 0x1C, 0x4D, 0x00}
84 { /* Set panel driving timing */
86 0xB4, 0x00, 0xFF, 0x51, 0x5A, 0x59, 0x5A, 0x03,
87 0x5A, 0x01, 0x70, 0x01, 0x70}
89 {LCM_SEND(2), {0xBC, 0x07}},
92 0xBF, 0x41, 0x0E, 0x01}
96 0xD3, 0x00, 0x0F, 0x00, 0x40, 0x07, 0x10, 0x00,
97 0x08, 0x10, 0x08, 0x00, 0x08, 0x54, 0x15, 0x0E,
98 0x05, 0x0E, 0x02, 0x15, 0x06, 0x05, 0x06, 0x47,
99 0x44, 0x0A, 0x0A, 0x4B, 0x10, 0x07, 0x07, 0x08,
100 0x00, 0x00, 0x00, 0x0A, 0x00, 0x01}
103 LCM_SEND(47), {45, 0,
104 0xD5, 0x1A, 0x1A, 0x1B, 0x1B, 0x00, 0x01, 0x02,
105 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
106 0x0B, 0x24, 0x25, 0x18, 0x18, 0x26, 0x27, 0x18,
107 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
108 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20,
109 0x21, 0x18, 0x18, 0x18, 0x18}
112 LCM_SEND(47), {45, 0,
113 0xD6, 0x1A, 0x1A, 0x1B, 0x1B, 0x0B, 0x0A, 0x09,
114 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
115 0x00, 0x21, 0x20, 0x58, 0x58, 0x27, 0x26, 0x18,
116 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
117 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x25,
118 0x24, 0x18, 0x18, 0x18, 0x18}
120 /* SETPANEL CCh: can set normally-white/black panel */
121 {LCM_SEND(2), {0xCC, 0x09}},
128 0xC7, 0x00, 0xC0, 0x40, 0xC0}
134 { /* Analog gamma setting */
135 LCM_SEND(45), {43, 0,
136 0xE0, 0x00, 0x0A, 0x0F, 0x24, 0x3A, 0x3F, 0x20,
137 0x3B, 0x08, 0x0D, 0x0E, 0x16, 0x0F, 0x12, 0x15,
138 0x13, 0x15, 0x09, 0x12, 0x12, 0x18, 0x00, 0x0A,
139 0x0F, 0x24, 0x3A, 0x3F, 0x20, 0x3B, 0x08, 0x0D,
140 0x0E, 0x16, 0x0F, 0x12, 0x15, 0x13, 0x15, 0x09,
144 {LCM_SEND(2), {0xBD, 0x00}}, /* BANK: RED */
146 LCM_SEND(46), {44, 0,
147 0xC1, 0x01, 0x00, 0x06, 0x0C, 0x14, 0x1D, 0x27,
148 0x2F, 0x38, 0x41, 0x49, 0x51, 0x59, 0x61, 0x69,
149 0x71, 0x79, 0x81, 0x89, 0x91, 0x99, 0xA1, 0xA9,
150 0xB2, 0xB9, 0xC1, 0xCA, 0xD1, 0xD8, 0xE2, 0xEA,
151 0xF0, 0xF7, 0xFF, 0x38, 0xFC, 0x3F, 0x0B, 0xC1,
152 0x13, 0xF1, 0x0D, 0xC0}
154 {LCM_SEND(2), {0xBD, 0x01}}, /* BANK: GREEN */
156 LCM_SEND(45), {43, 0,
157 0xC1, 0x00, 0x06, 0x0C, 0x14, 0x1D, 0x27, 0x2F,
158 0x38, 0x41, 0x49, 0x51, 0x59, 0x61, 0x69, 0x71,
159 0x79, 0x81, 0x89, 0x91, 0x99, 0xA1, 0xA9, 0xB2,
160 0xB9, 0xC1, 0xCA, 0xD1, 0xD8, 0xE2, 0xEA, 0xF0,
161 0xF7, 0xFF, 0x38, 0xFC, 0x3F, 0x0B, 0xC1, 0x13,
164 {LCM_SEND(2), {0xBD, 0x02}}, /* BANK: BLUE */
166 LCM_SEND(45), {43, 0,
167 0xC1, 0x00, 0x06, 0x0C, 0x14, 0x1D, 0x27, 0x2F,
168 0x38, 0x41, 0x49, 0x51, 0x59, 0x61, 0x69, 0x71,
169 0x79, 0x81, 0x89, 0x91, 0x99, 0xA1, 0xA9, 0xB2,
170 0xB9, 0xC1, 0xCA, 0xD1, 0xD8, 0xE2, 0xEA, 0xF0,
171 0xF7, 0xFF, 0x38, 0xFC, 0x3F, 0x0B, 0xC1, 0x13,
175 /* Set_address_mode (36h): RGB order and source/gate dir */
176 {LCM_SEND(2), {0x36, 0x03}},
179 {LCM_SEND(1), {0x11}},
181 {LCM_SEND(1), {0x29}},
185 static struct panel_cmds sleep_in[] = {
186 {LCM_SEND(1), {0x28}},
188 {LCM_SEND(1), {0x10}},
192 static struct panel_cmds sleep_out[] = {
193 {LCM_SEND(1), {0x11}},
195 {LCM_SEND(1), {0x29}},
199 int sprdfb_dsi_tx_cmds(struct panel_spec *panel,
200 struct panel_cmds *cmds, u32 cmds_len, u32 back2hs)
203 struct ops_mipi *ops = panel->info.mipi->ops;
204 u16 work_mode = panel->info.mipi->work_mode;
205 u8 write_type = (cmds->tag >> 16) & 0xff;
207 pr_info("%s, write data len: %d\n", __func__, cmds_len);
209 if (!ops->mipi_set_cmd_mode ||
210 !ops->mipi_dcs_write ||
211 !ops->mipi_force_write) {
212 pr_err("%s: Expected functions are NULL.\n", __func__);
216 ops->mipi_set_cmd_mode();
218 for (i = 0; i < cmds_len; i++) {
219 if (cmds->tag & LCM_TAG_SEND) {
221 ops->mipi_force_write(write_type, cmds->data,
222 cmds->tag & LCM_TAG_MASK);
224 ops->mipi_dcs_write(cmds->data, cmds->tag & LCM_TAG_MASK);
226 } else if (cmds->tag & LCM_TAG_SLEEP) {
227 time = (cmds->tag & LCM_TAG_MASK) * 1000;
228 usleep_range(time, time);
236 int sprdfb_dsi_rx_cmds(struct panel_spec *panel,
237 u8 regs, u16 get_len, u8 *data, u32 back2hs)
240 struct ops_mipi *ops = panel->info.mipi->ops;
242 u16 work_mode = panel->info.mipi->work_mode;
244 pr_info("%s, read data len: %d\n", __func__, get_len);
246 pr_err("Expected registers or data is NULL.\n");
249 if (!ops->mipi_set_cmd_mode ||
250 !ops->mipi_force_read ||
251 !ops->mipi_force_write) {
252 pr_err("%s: Expected functions are NULL.\n", __func__);
256 pkt_size[0] = get_len & 0xff;
257 pkt_size[1] = (get_len >> 8) & 0xff;
258 ops->mipi_set_cmd_mode();
259 ops->mipi_force_write(MAX_RX_PKT_SIZE, pkt_size, sizeof(pkt_size));
261 ret = ops->mipi_force_read(regs, get_len, data);
263 pr_err("%s: dsi read id fail\n", __func__);
270 int sprdfb_dsi_panel_sleep(struct panel_spec *panel, u8 is_sleep)
273 struct panel_cmds *sleep_inout;
276 sleep_inout = sleep_in;
277 len = ARRAY_SIZE(sleep_in);
279 sleep_inout = sleep_out;
280 len = ARRAY_SIZE(sleep_out);
283 return sprdfb_dsi_tx_cmds(panel, sleep_inout, len, false);
286 u32 sprdfb_dsi_panel_readid(struct panel_spec *panel)
288 int i, cnt = PANEL_ID_READ_CNT;
289 u8 regs = PANEL_ID_REG;
290 u8 data[PANEL_ID_LENGTH] = { 0 };
291 u8 id[PANEL_ID_LENGTH] = { 0 };
294 id[i++] = (PANEL_RETURN_ID >> 8) & 0xFF;
295 id[i] = PANEL_RETURN_ID & 0xFF;
297 sprdfb_dsi_rx_cmds(panel, regs, PANEL_ID_LENGTH, data, false);
298 pr_debug("%s: reading panel id(0x%2x) is ", __func__, regs);
299 for (i = 0; i < PANEL_ID_LENGTH; ++i) {
300 pr_debug("0x%2x, ", data[i]);
304 if (data[i] == id[i] && data[i + 1] == id[i + 1])
305 return PANEL_ACTUAL_ID;
311 static int hx8394d_dsi_panel_init(struct panel_spec *panel)
313 return sprdfb_dsi_tx_cmds(panel, hx8394d_init_cmds,
314 ARRAY_SIZE(hx8394d_init_cmds), false);
317 static int hx8394d_dsi_panel_dead(struct panel_spec *panel)
323 static struct panel_operations lcd_hx8394d_mipi_ops = {
324 .panel_init = hx8394d_dsi_panel_init,
325 .panel_readid = sprdfb_dsi_panel_readid,
326 .panel_enter_sleep = sprdfb_dsi_panel_sleep,
329 static struct timing_rgb lcd_hx8394d_mipi_timing = {
330 .hfp = 16, /* unit: pixel */
333 .vfp = 12, /*unit: line */
338 static struct info_mipi lcd_hx8394d_mipi_info = {
339 .work_mode = SPRDFB_MIPI_MODE_VIDEO,
340 .video_bus_width = 24,
343 .h_sync_pol = SPRDFB_POLARITY_POS,
344 .v_sync_pol = SPRDFB_POLARITY_POS,
345 .de_pol = SPRDFB_POLARITY_POS,
346 .te_pol = SPRDFB_POLARITY_POS,
347 .color_mode_pol = SPRDFB_POLARITY_POS,
348 .shut_down_pol = SPRDFB_POLARITY_POS,
349 .timing = &lcd_hx8394d_mipi_timing,
353 struct panel_spec lcd_hx8394d_mipi_spec = {
357 .type = LCD_MODE_DSI,
358 .direction = LCD_DIRECT_NORMAL,
360 .mipi = &lcd_hx8394d_mipi_info
362 .ops = &lcd_hx8394d_mipi_ops,