diff options
Diffstat (limited to 'drivers/video/fbdev/exynos')
-rw-r--r-- | drivers/video/fbdev/exynos/Kconfig | 32 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/Makefile | 7 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi.c | 574 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c | 880 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h | 46 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c | 618 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h | 112 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h | 149 | ||||
-rw-r--r-- | drivers/video/fbdev/exynos/s6e8ax0.c | 898 |
9 files changed, 3316 insertions, 0 deletions
diff --git a/drivers/video/fbdev/exynos/Kconfig b/drivers/video/fbdev/exynos/Kconfig new file mode 100644 index 000000000000..fcf2d48ac6d1 --- /dev/null +++ b/drivers/video/fbdev/exynos/Kconfig | |||
@@ -0,0 +1,32 @@ | |||
1 | # | ||
2 | # Exynos Video configuration | ||
3 | # | ||
4 | |||
5 | menuconfig EXYNOS_VIDEO | ||
6 | bool "Exynos Video driver support" | ||
7 | help | ||
8 | This enables support for EXYNOS Video device. | ||
9 | |||
10 | if EXYNOS_VIDEO | ||
11 | |||
12 | # | ||
13 | # MIPI DSI driver | ||
14 | # | ||
15 | |||
16 | config EXYNOS_MIPI_DSI | ||
17 | bool "EXYNOS MIPI DSI driver support." | ||
18 | depends on ARCH_S5PV210 || ARCH_EXYNOS | ||
19 | select GENERIC_PHY | ||
20 | help | ||
21 | This enables support for MIPI-DSI device. | ||
22 | |||
23 | config EXYNOS_LCD_S6E8AX0 | ||
24 | bool "S6E8AX0 MIPI AMOLED LCD Driver" | ||
25 | depends on EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE | ||
26 | depends on (LCD_CLASS_DEVICE = y) | ||
27 | default n | ||
28 | help | ||
29 | If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its | ||
30 | LCD control driver. | ||
31 | |||
32 | endif # EXYNOS_VIDEO | ||
diff --git a/drivers/video/fbdev/exynos/Makefile b/drivers/video/fbdev/exynos/Makefile new file mode 100644 index 000000000000..b5b1bd228abb --- /dev/null +++ b/drivers/video/fbdev/exynos/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # | ||
2 | # Makefile for the exynos video drivers. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ | ||
6 | exynos_mipi_dsi_lowlevel.o | ||
7 | obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi.c new file mode 100644 index 000000000000..cee9602f9a7b --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi.c | |||
@@ -0,0 +1,574 @@ | |||
1 | /* linux/drivers/video/exynos/exynos_mipi_dsi.c | ||
2 | * | ||
3 | * Samsung SoC MIPI-DSIM driver. | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae, <inki.dae@samsung.com> | ||
8 | * Donghwa Lee, <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/mutex.h> | ||
20 | #include <linux/wait.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/fb.h> | ||
24 | #include <linux/ctype.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <linux/irq.h> | ||
28 | #include <linux/memory.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/kthread.h> | ||
32 | #include <linux/notifier.h> | ||
33 | #include <linux/phy/phy.h> | ||
34 | #include <linux/regulator/consumer.h> | ||
35 | #include <linux/pm_runtime.h> | ||
36 | #include <linux/err.h> | ||
37 | |||
38 | #include <video/exynos_mipi_dsim.h> | ||
39 | |||
40 | #include "exynos_mipi_dsi_common.h" | ||
41 | #include "exynos_mipi_dsi_lowlevel.h" | ||
42 | |||
43 | struct mipi_dsim_ddi { | ||
44 | int bus_id; | ||
45 | struct list_head list; | ||
46 | struct mipi_dsim_lcd_device *dsim_lcd_dev; | ||
47 | struct mipi_dsim_lcd_driver *dsim_lcd_drv; | ||
48 | }; | ||
49 | |||
50 | static LIST_HEAD(dsim_ddi_list); | ||
51 | |||
52 | static DEFINE_MUTEX(mipi_dsim_lock); | ||
53 | |||
54 | static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device | ||
55 | *pdev) | ||
56 | { | ||
57 | return pdev->dev.platform_data; | ||
58 | } | ||
59 | |||
60 | static struct regulator_bulk_data supplies[] = { | ||
61 | { .supply = "vdd11", }, | ||
62 | { .supply = "vdd18", }, | ||
63 | }; | ||
64 | |||
65 | static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim) | ||
66 | { | ||
67 | int ret; | ||
68 | |||
69 | mutex_lock(&dsim->lock); | ||
70 | ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); | ||
71 | mutex_unlock(&dsim->lock); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim) | ||
77 | { | ||
78 | int ret; | ||
79 | |||
80 | mutex_lock(&dsim->lock); | ||
81 | ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); | ||
82 | mutex_unlock(&dsim->lock); | ||
83 | |||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | /* update all register settings to MIPI DSI controller. */ | ||
88 | static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) | ||
89 | { | ||
90 | /* | ||
91 | * data from Display controller(FIMD) is not transferred in video mode | ||
92 | * but in case of command mode, all settings is not updated to | ||
93 | * registers. | ||
94 | */ | ||
95 | exynos_mipi_dsi_stand_by(dsim, 0); | ||
96 | |||
97 | exynos_mipi_dsi_init_dsim(dsim); | ||
98 | exynos_mipi_dsi_init_link(dsim); | ||
99 | |||
100 | exynos_mipi_dsi_set_hs_enable(dsim); | ||
101 | |||
102 | /* set display timing. */ | ||
103 | exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); | ||
104 | |||
105 | exynos_mipi_dsi_init_interrupt(dsim); | ||
106 | |||
107 | /* | ||
108 | * data from Display controller(FIMD) is transferred in video mode | ||
109 | * but in case of command mode, all settings are updated to registers. | ||
110 | */ | ||
111 | exynos_mipi_dsi_stand_by(dsim, 1); | ||
112 | } | ||
113 | |||
114 | static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim, | ||
115 | int power) | ||
116 | { | ||
117 | struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; | ||
118 | struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; | ||
119 | |||
120 | switch (power) { | ||
121 | case FB_BLANK_POWERDOWN: | ||
122 | if (dsim->suspended) | ||
123 | return 0; | ||
124 | |||
125 | if (client_drv && client_drv->suspend) | ||
126 | client_drv->suspend(client_dev); | ||
127 | |||
128 | clk_disable(dsim->clock); | ||
129 | |||
130 | exynos_mipi_regulator_disable(dsim); | ||
131 | |||
132 | dsim->suspended = true; | ||
133 | |||
134 | break; | ||
135 | default: | ||
136 | break; | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power) | ||
143 | { | ||
144 | struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; | ||
145 | struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; | ||
146 | |||
147 | switch (power) { | ||
148 | case FB_BLANK_UNBLANK: | ||
149 | if (!dsim->suspended) | ||
150 | return 0; | ||
151 | |||
152 | /* lcd panel power on. */ | ||
153 | if (client_drv && client_drv->power_on) | ||
154 | client_drv->power_on(client_dev, 1); | ||
155 | |||
156 | exynos_mipi_regulator_enable(dsim); | ||
157 | |||
158 | /* enable MIPI-DSI PHY. */ | ||
159 | phy_power_on(dsim->phy); | ||
160 | |||
161 | clk_enable(dsim->clock); | ||
162 | |||
163 | exynos_mipi_update_cfg(dsim); | ||
164 | |||
165 | /* set lcd panel sequence commands. */ | ||
166 | if (client_drv && client_drv->set_sequence) | ||
167 | client_drv->set_sequence(client_dev); | ||
168 | |||
169 | dsim->suspended = false; | ||
170 | |||
171 | break; | ||
172 | case FB_BLANK_NORMAL: | ||
173 | /* TODO. */ | ||
174 | break; | ||
175 | default: | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) | ||
183 | { | ||
184 | struct mipi_dsim_ddi *dsim_ddi; | ||
185 | |||
186 | if (!lcd_dev->name) { | ||
187 | pr_err("dsim_lcd_device name is NULL.\n"); | ||
188 | return -EFAULT; | ||
189 | } | ||
190 | |||
191 | dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); | ||
192 | if (!dsim_ddi) { | ||
193 | pr_err("failed to allocate dsim_ddi object.\n"); | ||
194 | return -ENOMEM; | ||
195 | } | ||
196 | |||
197 | dsim_ddi->dsim_lcd_dev = lcd_dev; | ||
198 | |||
199 | mutex_lock(&mipi_dsim_lock); | ||
200 | list_add_tail(&dsim_ddi->list, &dsim_ddi_list); | ||
201 | mutex_unlock(&mipi_dsim_lock); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device( | ||
207 | struct mipi_dsim_lcd_driver *lcd_drv) | ||
208 | { | ||
209 | struct mipi_dsim_ddi *dsim_ddi, *next; | ||
210 | struct mipi_dsim_lcd_device *lcd_dev; | ||
211 | |||
212 | mutex_lock(&mipi_dsim_lock); | ||
213 | |||
214 | list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { | ||
215 | if (!dsim_ddi) | ||
216 | goto out; | ||
217 | |||
218 | lcd_dev = dsim_ddi->dsim_lcd_dev; | ||
219 | if (!lcd_dev) | ||
220 | continue; | ||
221 | |||
222 | if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { | ||
223 | /** | ||
224 | * bus_id would be used to identify | ||
225 | * connected bus. | ||
226 | */ | ||
227 | dsim_ddi->bus_id = lcd_dev->bus_id; | ||
228 | mutex_unlock(&mipi_dsim_lock); | ||
229 | |||
230 | return dsim_ddi; | ||
231 | } | ||
232 | |||
233 | list_del(&dsim_ddi->list); | ||
234 | kfree(dsim_ddi); | ||
235 | } | ||
236 | |||
237 | out: | ||
238 | mutex_unlock(&mipi_dsim_lock); | ||
239 | |||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) | ||
244 | { | ||
245 | struct mipi_dsim_ddi *dsim_ddi; | ||
246 | |||
247 | if (!lcd_drv->name) { | ||
248 | pr_err("dsim_lcd_driver name is NULL.\n"); | ||
249 | return -EFAULT; | ||
250 | } | ||
251 | |||
252 | dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); | ||
253 | if (!dsim_ddi) { | ||
254 | pr_err("mipi_dsim_ddi object not found.\n"); | ||
255 | return -EFAULT; | ||
256 | } | ||
257 | |||
258 | dsim_ddi->dsim_lcd_drv = lcd_drv; | ||
259 | |||
260 | pr_info("registered panel driver(%s) to mipi-dsi driver.\n", | ||
261 | lcd_drv->name); | ||
262 | |||
263 | return 0; | ||
264 | |||
265 | } | ||
266 | |||
267 | static struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi( | ||
268 | struct mipi_dsim_device *dsim, | ||
269 | const char *name) | ||
270 | { | ||
271 | struct mipi_dsim_ddi *dsim_ddi, *next; | ||
272 | struct mipi_dsim_lcd_driver *lcd_drv; | ||
273 | struct mipi_dsim_lcd_device *lcd_dev; | ||
274 | int ret; | ||
275 | |||
276 | mutex_lock(&dsim->lock); | ||
277 | |||
278 | list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { | ||
279 | lcd_drv = dsim_ddi->dsim_lcd_drv; | ||
280 | lcd_dev = dsim_ddi->dsim_lcd_dev; | ||
281 | if (!lcd_drv || !lcd_dev || | ||
282 | (dsim->id != dsim_ddi->bus_id)) | ||
283 | continue; | ||
284 | |||
285 | dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n", | ||
286 | lcd_drv->id, lcd_dev->id); | ||
287 | dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n", | ||
288 | lcd_dev->bus_id, dsim->id); | ||
289 | |||
290 | if ((strcmp(lcd_drv->name, name) == 0)) { | ||
291 | lcd_dev->master = dsim; | ||
292 | |||
293 | lcd_dev->dev.parent = dsim->dev; | ||
294 | dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name); | ||
295 | |||
296 | ret = device_register(&lcd_dev->dev); | ||
297 | if (ret < 0) { | ||
298 | dev_err(dsim->dev, | ||
299 | "can't register %s, status %d\n", | ||
300 | dev_name(&lcd_dev->dev), ret); | ||
301 | mutex_unlock(&dsim->lock); | ||
302 | |||
303 | return NULL; | ||
304 | } | ||
305 | |||
306 | dsim->dsim_lcd_dev = lcd_dev; | ||
307 | dsim->dsim_lcd_drv = lcd_drv; | ||
308 | |||
309 | mutex_unlock(&dsim->lock); | ||
310 | |||
311 | return dsim_ddi; | ||
312 | } | ||
313 | } | ||
314 | |||
315 | mutex_unlock(&dsim->lock); | ||
316 | |||
317 | return NULL; | ||
318 | } | ||
319 | |||
320 | /* define MIPI-DSI Master operations. */ | ||
321 | static struct mipi_dsim_master_ops master_ops = { | ||
322 | .cmd_read = exynos_mipi_dsi_rd_data, | ||
323 | .cmd_write = exynos_mipi_dsi_wr_data, | ||
324 | .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status, | ||
325 | .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done, | ||
326 | .set_early_blank_mode = exynos_mipi_dsi_early_blank_mode, | ||
327 | .set_blank_mode = exynos_mipi_dsi_blank_mode, | ||
328 | }; | ||
329 | |||
330 | static int exynos_mipi_dsi_probe(struct platform_device *pdev) | ||
331 | { | ||
332 | struct resource *res; | ||
333 | struct mipi_dsim_device *dsim; | ||
334 | struct mipi_dsim_config *dsim_config; | ||
335 | struct mipi_dsim_platform_data *dsim_pd; | ||
336 | struct mipi_dsim_ddi *dsim_ddi; | ||
337 | int ret = -EINVAL; | ||
338 | |||
339 | dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device), | ||
340 | GFP_KERNEL); | ||
341 | if (!dsim) { | ||
342 | dev_err(&pdev->dev, "failed to allocate dsim object.\n"); | ||
343 | return -ENOMEM; | ||
344 | } | ||
345 | |||
346 | dsim->pd = to_dsim_plat(pdev); | ||
347 | dsim->dev = &pdev->dev; | ||
348 | dsim->id = pdev->id; | ||
349 | |||
350 | /* get mipi_dsim_platform_data. */ | ||
351 | dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; | ||
352 | if (dsim_pd == NULL) { | ||
353 | dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); | ||
354 | return -EINVAL; | ||
355 | } | ||
356 | /* get mipi_dsim_config. */ | ||
357 | dsim_config = dsim_pd->dsim_config; | ||
358 | if (dsim_config == NULL) { | ||
359 | dev_err(&pdev->dev, "failed to get dsim config data.\n"); | ||
360 | return -EINVAL; | ||
361 | } | ||
362 | |||
363 | dsim->dsim_config = dsim_config; | ||
364 | dsim->master_ops = &master_ops; | ||
365 | |||
366 | mutex_init(&dsim->lock); | ||
367 | |||
368 | ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), | ||
369 | supplies); | ||
370 | if (ret) { | ||
371 | dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret); | ||
372 | return ret; | ||
373 | } | ||
374 | |||
375 | dsim->phy = devm_phy_get(&pdev->dev, "dsim"); | ||
376 | if (IS_ERR(dsim->phy)) | ||
377 | return PTR_ERR(dsim->phy); | ||
378 | |||
379 | dsim->clock = devm_clk_get(&pdev->dev, "dsim0"); | ||
380 | if (IS_ERR(dsim->clock)) { | ||
381 | dev_err(&pdev->dev, "failed to get dsim clock source\n"); | ||
382 | return -ENODEV; | ||
383 | } | ||
384 | |||
385 | clk_enable(dsim->clock); | ||
386 | |||
387 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
388 | |||
389 | dsim->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
390 | if (IS_ERR(dsim->reg_base)) { | ||
391 | ret = PTR_ERR(dsim->reg_base); | ||
392 | goto error; | ||
393 | } | ||
394 | |||
395 | mutex_init(&dsim->lock); | ||
396 | |||
397 | /* bind lcd ddi matched with panel name. */ | ||
398 | dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); | ||
399 | if (!dsim_ddi) { | ||
400 | dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); | ||
401 | ret = -EINVAL; | ||
402 | goto error; | ||
403 | } | ||
404 | |||
405 | dsim->irq = platform_get_irq(pdev, 0); | ||
406 | if (IS_ERR_VALUE(dsim->irq)) { | ||
407 | dev_err(&pdev->dev, "failed to request dsim irq resource\n"); | ||
408 | ret = -EINVAL; | ||
409 | goto error; | ||
410 | } | ||
411 | |||
412 | init_completion(&dsim_wr_comp); | ||
413 | init_completion(&dsim_rd_comp); | ||
414 | platform_set_drvdata(pdev, dsim); | ||
415 | |||
416 | ret = devm_request_irq(&pdev->dev, dsim->irq, | ||
417 | exynos_mipi_dsi_interrupt_handler, | ||
418 | IRQF_SHARED, dev_name(&pdev->dev), dsim); | ||
419 | if (ret != 0) { | ||
420 | dev_err(&pdev->dev, "failed to request dsim irq\n"); | ||
421 | ret = -EINVAL; | ||
422 | goto error; | ||
423 | } | ||
424 | |||
425 | /* enable interrupts */ | ||
426 | exynos_mipi_dsi_init_interrupt(dsim); | ||
427 | |||
428 | /* initialize mipi-dsi client(lcd panel). */ | ||
429 | if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) | ||
430 | dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); | ||
431 | |||
432 | /* in case mipi-dsi has been enabled by bootloader */ | ||
433 | if (dsim_pd->enabled) { | ||
434 | exynos_mipi_regulator_enable(dsim); | ||
435 | goto done; | ||
436 | } | ||
437 | |||
438 | /* lcd panel power on. */ | ||
439 | if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) | ||
440 | dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1); | ||
441 | |||
442 | exynos_mipi_regulator_enable(dsim); | ||
443 | |||
444 | /* enable MIPI-DSI PHY. */ | ||
445 | phy_power_on(dsim->phy); | ||
446 | |||
447 | exynos_mipi_update_cfg(dsim); | ||
448 | |||
449 | /* set lcd panel sequence commands. */ | ||
450 | if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence) | ||
451 | dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev); | ||
452 | |||
453 | dsim->suspended = false; | ||
454 | |||
455 | done: | ||
456 | platform_set_drvdata(pdev, dsim); | ||
457 | |||
458 | dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__, | ||
459 | dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); | ||
460 | |||
461 | return 0; | ||
462 | |||
463 | error: | ||
464 | clk_disable(dsim->clock); | ||
465 | return ret; | ||
466 | } | ||
467 | |||
468 | static int exynos_mipi_dsi_remove(struct platform_device *pdev) | ||
469 | { | ||
470 | struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); | ||
471 | struct mipi_dsim_ddi *dsim_ddi, *next; | ||
472 | struct mipi_dsim_lcd_driver *dsim_lcd_drv; | ||
473 | |||
474 | clk_disable(dsim->clock); | ||
475 | |||
476 | list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { | ||
477 | if (dsim_ddi) { | ||
478 | if (dsim->id != dsim_ddi->bus_id) | ||
479 | continue; | ||
480 | |||
481 | dsim_lcd_drv = dsim_ddi->dsim_lcd_drv; | ||
482 | |||
483 | if (dsim_lcd_drv->remove) | ||
484 | dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev); | ||
485 | |||
486 | kfree(dsim_ddi); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | #ifdef CONFIG_PM_SLEEP | ||
494 | static int exynos_mipi_dsi_suspend(struct device *dev) | ||
495 | { | ||
496 | struct platform_device *pdev = to_platform_device(dev); | ||
497 | struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); | ||
498 | struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; | ||
499 | struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; | ||
500 | |||
501 | disable_irq(dsim->irq); | ||
502 | |||
503 | if (dsim->suspended) | ||
504 | return 0; | ||
505 | |||
506 | if (client_drv && client_drv->suspend) | ||
507 | client_drv->suspend(client_dev); | ||
508 | |||
509 | /* disable MIPI-DSI PHY. */ | ||
510 | phy_power_off(dsim->phy); | ||
511 | |||
512 | clk_disable(dsim->clock); | ||
513 | |||
514 | exynos_mipi_regulator_disable(dsim); | ||
515 | |||
516 | dsim->suspended = true; | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int exynos_mipi_dsi_resume(struct device *dev) | ||
522 | { | ||
523 | struct platform_device *pdev = to_platform_device(dev); | ||
524 | struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); | ||
525 | struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; | ||
526 | struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; | ||
527 | |||
528 | enable_irq(dsim->irq); | ||
529 | |||
530 | if (!dsim->suspended) | ||
531 | return 0; | ||
532 | |||
533 | /* lcd panel power on. */ | ||
534 | if (client_drv && client_drv->power_on) | ||
535 | client_drv->power_on(client_dev, 1); | ||
536 | |||
537 | exynos_mipi_regulator_enable(dsim); | ||
538 | |||
539 | /* enable MIPI-DSI PHY. */ | ||
540 | phy_power_on(dsim->phy); | ||
541 | |||
542 | clk_enable(dsim->clock); | ||
543 | |||
544 | exynos_mipi_update_cfg(dsim); | ||
545 | |||
546 | /* set lcd panel sequence commands. */ | ||
547 | if (client_drv && client_drv->set_sequence) | ||
548 | client_drv->set_sequence(client_dev); | ||
549 | |||
550 | dsim->suspended = false; | ||
551 | |||
552 | return 0; | ||
553 | } | ||
554 | #endif | ||
555 | |||
556 | static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = { | ||
557 | SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume) | ||
558 | }; | ||
559 | |||
560 | static struct platform_driver exynos_mipi_dsi_driver = { | ||
561 | .probe = exynos_mipi_dsi_probe, | ||
562 | .remove = exynos_mipi_dsi_remove, | ||
563 | .driver = { | ||
564 | .name = "exynos-mipi-dsim", | ||
565 | .owner = THIS_MODULE, | ||
566 | .pm = &exynos_mipi_dsi_pm_ops, | ||
567 | }, | ||
568 | }; | ||
569 | |||
570 | module_platform_driver(exynos_mipi_dsi_driver); | ||
571 | |||
572 | MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); | ||
573 | MODULE_DESCRIPTION("Samusung SoC MIPI-DSI driver"); | ||
574 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c new file mode 100644 index 000000000000..85edabfdef5a --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c | |||
@@ -0,0 +1,880 @@ | |||
1 | /* linux/drivers/video/exynos/exynos_mipi_dsi_common.c | ||
2 | * | ||
3 | * Samsung SoC MIPI-DSI common driver. | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae, <inki.dae@samsung.com> | ||
8 | * Donghwa Lee, <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/wait.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/fb.h> | ||
23 | #include <linux/ctype.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/memory.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/irqreturn.h> | ||
29 | #include <linux/kthread.h> | ||
30 | |||
31 | #include <video/mipi_display.h> | ||
32 | #include <video/exynos_mipi_dsim.h> | ||
33 | |||
34 | #include "exynos_mipi_dsi_regs.h" | ||
35 | #include "exynos_mipi_dsi_lowlevel.h" | ||
36 | #include "exynos_mipi_dsi_common.h" | ||
37 | |||
38 | #define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250) | ||
39 | #define MIPI_RX_FIFO_READ_DONE 0x30800002 | ||
40 | #define MIPI_MAX_RX_FIFO 20 | ||
41 | #define MHZ (1000 * 1000) | ||
42 | #define FIN_HZ (24 * MHZ) | ||
43 | |||
44 | #define DFIN_PLL_MIN_HZ (6 * MHZ) | ||
45 | #define DFIN_PLL_MAX_HZ (12 * MHZ) | ||
46 | |||
47 | #define DFVCO_MIN_HZ (500 * MHZ) | ||
48 | #define DFVCO_MAX_HZ (1000 * MHZ) | ||
49 | |||
50 | #define TRY_GET_FIFO_TIMEOUT (5000 * 2) | ||
51 | #define TRY_FIFO_CLEAR (10) | ||
52 | |||
53 | /* MIPI-DSIM status types. */ | ||
54 | enum { | ||
55 | DSIM_STATE_INIT, /* should be initialized. */ | ||
56 | DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */ | ||
57 | DSIM_STATE_HSCLKEN, /* HS clock was enabled. */ | ||
58 | DSIM_STATE_ULPS | ||
59 | }; | ||
60 | |||
61 | /* define DSI lane types. */ | ||
62 | enum { | ||
63 | DSIM_LANE_CLOCK = (1 << 0), | ||
64 | DSIM_LANE_DATA0 = (1 << 1), | ||
65 | DSIM_LANE_DATA1 = (1 << 2), | ||
66 | DSIM_LANE_DATA2 = (1 << 3), | ||
67 | DSIM_LANE_DATA3 = (1 << 4) | ||
68 | }; | ||
69 | |||
70 | static unsigned int dpll_table[15] = { | ||
71 | 100, 120, 170, 220, 270, | ||
72 | 320, 390, 450, 510, 560, | ||
73 | 640, 690, 770, 870, 950 | ||
74 | }; | ||
75 | |||
76 | irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) | ||
77 | { | ||
78 | struct mipi_dsim_device *dsim = dev_id; | ||
79 | unsigned int intsrc, intmsk; | ||
80 | |||
81 | intsrc = exynos_mipi_dsi_read_interrupt(dsim); | ||
82 | intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); | ||
83 | intmsk = ~intmsk & intsrc; | ||
84 | |||
85 | if (intsrc & INTMSK_RX_DONE) { | ||
86 | complete(&dsim_rd_comp); | ||
87 | dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); | ||
88 | } | ||
89 | if (intsrc & INTMSK_FIFO_EMPTY) { | ||
90 | complete(&dsim_wr_comp); | ||
91 | dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); | ||
92 | } | ||
93 | |||
94 | exynos_mipi_dsi_clear_interrupt(dsim, intmsk); | ||
95 | |||
96 | return IRQ_HANDLED; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * write long packet to mipi dsi slave | ||
101 | * @dsim: mipi dsim device structure. | ||
102 | * @data0: packet data to send. | ||
103 | * @data1: size of packet data | ||
104 | */ | ||
105 | static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, | ||
106 | const unsigned char *data0, unsigned int data_size) | ||
107 | { | ||
108 | unsigned int data_cnt = 0, payload = 0; | ||
109 | |||
110 | /* in case that data count is more then 4 */ | ||
111 | for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { | ||
112 | /* | ||
113 | * after sending 4bytes per one time, | ||
114 | * send remainder data less then 4. | ||
115 | */ | ||
116 | if ((data_size - data_cnt) < 4) { | ||
117 | if ((data_size - data_cnt) == 3) { | ||
118 | payload = data0[data_cnt] | | ||
119 | data0[data_cnt + 1] << 8 | | ||
120 | data0[data_cnt + 2] << 16; | ||
121 | dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n", | ||
122 | payload, data0[data_cnt], | ||
123 | data0[data_cnt + 1], | ||
124 | data0[data_cnt + 2]); | ||
125 | } else if ((data_size - data_cnt) == 2) { | ||
126 | payload = data0[data_cnt] | | ||
127 | data0[data_cnt + 1] << 8; | ||
128 | dev_dbg(dsim->dev, | ||
129 | "count = 2 payload = %x, %x %x\n", payload, | ||
130 | data0[data_cnt], | ||
131 | data0[data_cnt + 1]); | ||
132 | } else if ((data_size - data_cnt) == 1) { | ||
133 | payload = data0[data_cnt]; | ||
134 | } | ||
135 | |||
136 | exynos_mipi_dsi_wr_tx_data(dsim, payload); | ||
137 | /* send 4bytes per one time. */ | ||
138 | } else { | ||
139 | payload = data0[data_cnt] | | ||
140 | data0[data_cnt + 1] << 8 | | ||
141 | data0[data_cnt + 2] << 16 | | ||
142 | data0[data_cnt + 3] << 24; | ||
143 | |||
144 | dev_dbg(dsim->dev, | ||
145 | "count = 4 payload = %x, %x %x %x %x\n", | ||
146 | payload, *(u8 *)(data0 + data_cnt), | ||
147 | data0[data_cnt + 1], | ||
148 | data0[data_cnt + 2], | ||
149 | data0[data_cnt + 3]); | ||
150 | |||
151 | exynos_mipi_dsi_wr_tx_data(dsim, payload); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, | ||
157 | const unsigned char *data0, unsigned int data_size) | ||
158 | { | ||
159 | unsigned int check_rx_ack = 0; | ||
160 | |||
161 | if (dsim->state == DSIM_STATE_ULPS) { | ||
162 | dev_err(dsim->dev, "state is ULPS.\n"); | ||
163 | |||
164 | return -EINVAL; | ||
165 | } | ||
166 | |||
167 | /* FIXME!!! why does it need this delay? */ | ||
168 | msleep(20); | ||
169 | |||
170 | mutex_lock(&dsim->lock); | ||
171 | |||
172 | switch (data_id) { | ||
173 | /* short packet types of packet types for command. */ | ||
174 | case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: | ||
175 | case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: | ||
176 | case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: | ||
177 | case MIPI_DSI_DCS_SHORT_WRITE: | ||
178 | case MIPI_DSI_DCS_SHORT_WRITE_PARAM: | ||
179 | case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: | ||
180 | exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); | ||
181 | if (check_rx_ack) { | ||
182 | /* process response func should be implemented */ | ||
183 | mutex_unlock(&dsim->lock); | ||
184 | return 0; | ||
185 | } else { | ||
186 | mutex_unlock(&dsim->lock); | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | |||
190 | /* general command */ | ||
191 | case MIPI_DSI_COLOR_MODE_OFF: | ||
192 | case MIPI_DSI_COLOR_MODE_ON: | ||
193 | case MIPI_DSI_SHUTDOWN_PERIPHERAL: | ||
194 | case MIPI_DSI_TURN_ON_PERIPHERAL: | ||
195 | exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); | ||
196 | if (check_rx_ack) { | ||
197 | /* process response func should be implemented. */ | ||
198 | mutex_unlock(&dsim->lock); | ||
199 | return 0; | ||
200 | } else { | ||
201 | mutex_unlock(&dsim->lock); | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | |||
205 | /* packet types for video data */ | ||
206 | case MIPI_DSI_V_SYNC_START: | ||
207 | case MIPI_DSI_V_SYNC_END: | ||
208 | case MIPI_DSI_H_SYNC_START: | ||
209 | case MIPI_DSI_H_SYNC_END: | ||
210 | case MIPI_DSI_END_OF_TRANSMISSION: | ||
211 | mutex_unlock(&dsim->lock); | ||
212 | return 0; | ||
213 | |||
214 | /* long packet type and null packet */ | ||
215 | case MIPI_DSI_NULL_PACKET: | ||
216 | case MIPI_DSI_BLANKING_PACKET: | ||
217 | mutex_unlock(&dsim->lock); | ||
218 | return 0; | ||
219 | case MIPI_DSI_GENERIC_LONG_WRITE: | ||
220 | case MIPI_DSI_DCS_LONG_WRITE: | ||
221 | { | ||
222 | unsigned int size, payload = 0; | ||
223 | reinit_completion(&dsim_wr_comp); | ||
224 | |||
225 | size = data_size * 4; | ||
226 | |||
227 | /* if data count is less then 4, then send 3bytes data. */ | ||
228 | if (data_size < 4) { | ||
229 | payload = data0[0] | | ||
230 | data0[1] << 8 | | ||
231 | data0[2] << 16; | ||
232 | |||
233 | exynos_mipi_dsi_wr_tx_data(dsim, payload); | ||
234 | |||
235 | dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n", | ||
236 | data_size, payload, data0[0], | ||
237 | data0[1], data0[2]); | ||
238 | |||
239 | /* in case that data count is more then 4 */ | ||
240 | } else | ||
241 | exynos_mipi_dsi_long_data_wr(dsim, data0, data_size); | ||
242 | |||
243 | /* put data into header fifo */ | ||
244 | exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff, | ||
245 | (data_size & 0xff00) >> 8); | ||
246 | |||
247 | if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp, | ||
248 | MIPI_FIFO_TIMEOUT)) { | ||
249 | dev_warn(dsim->dev, "command write timeout.\n"); | ||
250 | mutex_unlock(&dsim->lock); | ||
251 | return -EAGAIN; | ||
252 | } | ||
253 | |||
254 | if (check_rx_ack) { | ||
255 | /* process response func should be implemented. */ | ||
256 | mutex_unlock(&dsim->lock); | ||
257 | return 0; | ||
258 | } else { | ||
259 | mutex_unlock(&dsim->lock); | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | /* packet typo for video data */ | ||
265 | case MIPI_DSI_PACKED_PIXEL_STREAM_16: | ||
266 | case MIPI_DSI_PACKED_PIXEL_STREAM_18: | ||
267 | case MIPI_DSI_PIXEL_STREAM_3BYTE_18: | ||
268 | case MIPI_DSI_PACKED_PIXEL_STREAM_24: | ||
269 | if (check_rx_ack) { | ||
270 | /* process response func should be implemented. */ | ||
271 | mutex_unlock(&dsim->lock); | ||
272 | return 0; | ||
273 | } else { | ||
274 | mutex_unlock(&dsim->lock); | ||
275 | return -EINVAL; | ||
276 | } | ||
277 | default: | ||
278 | dev_warn(dsim->dev, | ||
279 | "data id %x is not supported current DSI spec.\n", | ||
280 | data_id); | ||
281 | |||
282 | mutex_unlock(&dsim->lock); | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim, | ||
288 | unsigned int req_size, unsigned int rx_data, u8 *rx_buf) | ||
289 | { | ||
290 | unsigned int rcv_pkt, i, j; | ||
291 | u16 rxsize; | ||
292 | |||
293 | /* for long packet */ | ||
294 | rxsize = (u16)((rx_data & 0x00ffff00) >> 8); | ||
295 | dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize); | ||
296 | if (rxsize != req_size) { | ||
297 | dev_dbg(dsim->dev, | ||
298 | "received size mismatch received: %d, requested: %d\n", | ||
299 | rxsize, req_size); | ||
300 | goto err; | ||
301 | } | ||
302 | |||
303 | for (i = 0; i < (rxsize >> 2); i++) { | ||
304 | rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); | ||
305 | dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); | ||
306 | for (j = 0; j < 4; j++) { | ||
307 | rx_buf[(i * 4) + j] = | ||
308 | (u8)(rcv_pkt >> (j * 8)) & 0xff; | ||
309 | dev_dbg(dsim->dev, "received value : %02x\n", | ||
310 | (rcv_pkt >> (j * 8)) & 0xff); | ||
311 | } | ||
312 | } | ||
313 | if (rxsize % 4) { | ||
314 | rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); | ||
315 | dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); | ||
316 | for (j = 0; j < (rxsize % 4); j++) { | ||
317 | rx_buf[(i * 4) + j] = | ||
318 | (u8)(rcv_pkt >> (j * 8)) & 0xff; | ||
319 | dev_dbg(dsim->dev, "received value : %02x\n", | ||
320 | (rcv_pkt >> (j * 8)) & 0xff); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | return rxsize; | ||
325 | |||
326 | err: | ||
327 | return -EINVAL; | ||
328 | } | ||
329 | |||
330 | static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size) | ||
331 | { | ||
332 | switch (req_size) { | ||
333 | case 1: | ||
334 | return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE; | ||
335 | case 2: | ||
336 | return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE; | ||
337 | default: | ||
338 | return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, | ||
343 | unsigned int data0, unsigned int req_size, u8 *rx_buf) | ||
344 | { | ||
345 | unsigned int rx_data, rcv_pkt, i; | ||
346 | u8 response = 0; | ||
347 | u16 rxsize; | ||
348 | |||
349 | if (dsim->state == DSIM_STATE_ULPS) { | ||
350 | dev_err(dsim->dev, "state is ULPS.\n"); | ||
351 | |||
352 | return -EINVAL; | ||
353 | } | ||
354 | |||
355 | /* FIXME!!! */ | ||
356 | msleep(20); | ||
357 | |||
358 | mutex_lock(&dsim->lock); | ||
359 | reinit_completion(&dsim_rd_comp); | ||
360 | exynos_mipi_dsi_rd_tx_header(dsim, | ||
361 | MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size); | ||
362 | |||
363 | response = exynos_mipi_dsi_response_size(req_size); | ||
364 | |||
365 | switch (data_id) { | ||
366 | case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: | ||
367 | case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: | ||
368 | case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: | ||
369 | case MIPI_DSI_DCS_READ: | ||
370 | exynos_mipi_dsi_rd_tx_header(dsim, | ||
371 | data_id, data0); | ||
372 | /* process response func should be implemented. */ | ||
373 | break; | ||
374 | default: | ||
375 | dev_warn(dsim->dev, | ||
376 | "data id %x is not supported current DSI spec.\n", | ||
377 | data_id); | ||
378 | |||
379 | mutex_unlock(&dsim->lock); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp, | ||
384 | MIPI_FIFO_TIMEOUT)) { | ||
385 | pr_err("RX done interrupt timeout\n"); | ||
386 | mutex_unlock(&dsim->lock); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | msleep(20); | ||
391 | |||
392 | rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim); | ||
393 | |||
394 | if ((u8)(rx_data & 0xff) != response) { | ||
395 | printk(KERN_ERR | ||
396 | "mipi dsi wrong response rx_data : %x, response:%x\n", | ||
397 | rx_data, response); | ||
398 | goto clear_rx_fifo; | ||
399 | } | ||
400 | |||
401 | if (req_size <= 2) { | ||
402 | /* for short packet */ | ||
403 | for (i = 0; i < req_size; i++) | ||
404 | rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff; | ||
405 | rxsize = req_size; | ||
406 | } else { | ||
407 | /* for long packet */ | ||
408 | rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data, | ||
409 | rx_buf); | ||
410 | if (rxsize != req_size) | ||
411 | goto clear_rx_fifo; | ||
412 | } | ||
413 | |||
414 | rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); | ||
415 | |||
416 | msleep(20); | ||
417 | |||
418 | if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) { | ||
419 | dev_info(dsim->dev, | ||
420 | "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt); | ||
421 | goto clear_rx_fifo; | ||
422 | } | ||
423 | |||
424 | mutex_unlock(&dsim->lock); | ||
425 | |||
426 | return rxsize; | ||
427 | |||
428 | clear_rx_fifo: | ||
429 | i = 0; | ||
430 | while (1) { | ||
431 | rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); | ||
432 | if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE) | ||
433 | || (i > MIPI_MAX_RX_FIFO)) | ||
434 | break; | ||
435 | dev_dbg(dsim->dev, | ||
436 | "mipi dsi clear rx fifo : %08x\n", rcv_pkt); | ||
437 | i++; | ||
438 | } | ||
439 | dev_info(dsim->dev, | ||
440 | "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt); | ||
441 | |||
442 | mutex_unlock(&dsim->lock); | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, | ||
448 | unsigned int enable) | ||
449 | { | ||
450 | int sw_timeout; | ||
451 | |||
452 | if (enable) { | ||
453 | sw_timeout = 1000; | ||
454 | |||
455 | exynos_mipi_dsi_enable_pll(dsim, 1); | ||
456 | while (1) { | ||
457 | sw_timeout--; | ||
458 | if (exynos_mipi_dsi_is_pll_stable(dsim)) | ||
459 | return 0; | ||
460 | if (sw_timeout == 0) | ||
461 | return -EINVAL; | ||
462 | } | ||
463 | } else | ||
464 | exynos_mipi_dsi_enable_pll(dsim, 0); | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, | ||
470 | unsigned int pre_divider, unsigned int main_divider, | ||
471 | unsigned int scaler) | ||
472 | { | ||
473 | unsigned long dfin_pll, dfvco, dpll_out; | ||
474 | unsigned int i, freq_band = 0xf; | ||
475 | |||
476 | dfin_pll = (FIN_HZ / pre_divider); | ||
477 | |||
478 | /****************************************************** | ||
479 | * Serial Clock(=ByteClk X 8) FreqBand[3:0] * | ||
480 | ****************************************************** | ||
481 | * ~ 99.99 MHz 0000 | ||
482 | * 100 ~ 119.99 MHz 0001 | ||
483 | * 120 ~ 159.99 MHz 0010 | ||
484 | * 160 ~ 199.99 MHz 0011 | ||
485 | * 200 ~ 239.99 MHz 0100 | ||
486 | * 140 ~ 319.99 MHz 0101 | ||
487 | * 320 ~ 389.99 MHz 0110 | ||
488 | * 390 ~ 449.99 MHz 0111 | ||
489 | * 450 ~ 509.99 MHz 1000 | ||
490 | * 510 ~ 559.99 MHz 1001 | ||
491 | * 560 ~ 639.99 MHz 1010 | ||
492 | * 640 ~ 689.99 MHz 1011 | ||
493 | * 690 ~ 769.99 MHz 1100 | ||
494 | * 770 ~ 869.99 MHz 1101 | ||
495 | * 870 ~ 949.99 MHz 1110 | ||
496 | * 950 ~ 1000 MHz 1111 | ||
497 | ******************************************************/ | ||
498 | if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { | ||
499 | dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n"); | ||
500 | exynos_mipi_dsi_enable_afc(dsim, 0, 0); | ||
501 | } else { | ||
502 | if (dfin_pll < 7 * MHZ) | ||
503 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); | ||
504 | else if (dfin_pll < 8 * MHZ) | ||
505 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); | ||
506 | else if (dfin_pll < 9 * MHZ) | ||
507 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); | ||
508 | else if (dfin_pll < 10 * MHZ) | ||
509 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); | ||
510 | else if (dfin_pll < 11 * MHZ) | ||
511 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); | ||
512 | else | ||
513 | exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); | ||
514 | } | ||
515 | |||
516 | dfvco = dfin_pll * main_divider; | ||
517 | dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", | ||
518 | dfvco, dfin_pll, main_divider); | ||
519 | if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) | ||
520 | dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n"); | ||
521 | |||
522 | dpll_out = dfvco / (1 << scaler); | ||
523 | dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n", | ||
524 | dpll_out, dfvco, scaler); | ||
525 | |||
526 | for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { | ||
527 | if (dpll_out < dpll_table[i] * MHZ) { | ||
528 | freq_band = i; | ||
529 | break; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | dev_dbg(dsim->dev, "freq_band = %d\n", freq_band); | ||
534 | |||
535 | exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); | ||
536 | |||
537 | exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); | ||
538 | exynos_mipi_dsi_prep_ctrl(dsim, 0); | ||
539 | |||
540 | /* Freq Band */ | ||
541 | exynos_mipi_dsi_pll_freq_band(dsim, freq_band); | ||
542 | |||
543 | /* Stable time */ | ||
544 | exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time); | ||
545 | |||
546 | /* Enable PLL */ | ||
547 | dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n", | ||
548 | (dpll_out / MHZ)); | ||
549 | |||
550 | return dpll_out; | ||
551 | } | ||
552 | |||
553 | static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, | ||
554 | unsigned int byte_clk_sel, unsigned int enable) | ||
555 | { | ||
556 | unsigned int esc_div; | ||
557 | unsigned long esc_clk_error_rate; | ||
558 | unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; | ||
559 | |||
560 | if (enable) { | ||
561 | dsim->e_clk_src = byte_clk_sel; | ||
562 | |||
563 | /* Escape mode clock and byte clock source */ | ||
564 | exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); | ||
565 | |||
566 | /* DPHY, DSIM Link : D-PHY clock out */ | ||
567 | if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { | ||
568 | hs_clk = exynos_mipi_dsi_change_pll(dsim, | ||
569 | dsim->dsim_config->p, dsim->dsim_config->m, | ||
570 | dsim->dsim_config->s); | ||
571 | if (hs_clk == 0) { | ||
572 | dev_err(dsim->dev, | ||
573 | "failed to get hs clock.\n"); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | byte_clk = hs_clk / 8; | ||
578 | exynos_mipi_dsi_enable_pll_bypass(dsim, 0); | ||
579 | exynos_mipi_dsi_pll_on(dsim, 1); | ||
580 | /* DPHY : D-PHY clock out, DSIM link : external clock out */ | ||
581 | } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) { | ||
582 | dev_warn(dsim->dev, "this project is not support\n"); | ||
583 | dev_warn(dsim->dev, | ||
584 | "external clock source for MIPI DSIM.\n"); | ||
585 | } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) { | ||
586 | dev_warn(dsim->dev, "this project is not support\n"); | ||
587 | dev_warn(dsim->dev, | ||
588 | "external clock source for MIPI DSIM\n"); | ||
589 | } | ||
590 | |||
591 | /* escape clock divider */ | ||
592 | esc_div = byte_clk / (dsim->dsim_config->esc_clk); | ||
593 | dev_dbg(dsim->dev, | ||
594 | "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", | ||
595 | esc_div, byte_clk, dsim->dsim_config->esc_clk); | ||
596 | if ((byte_clk / esc_div) >= (20 * MHZ) || | ||
597 | (byte_clk / esc_div) > | ||
598 | dsim->dsim_config->esc_clk) | ||
599 | esc_div += 1; | ||
600 | |||
601 | escape_clk = byte_clk / esc_div; | ||
602 | dev_dbg(dsim->dev, | ||
603 | "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", | ||
604 | escape_clk, byte_clk, esc_div); | ||
605 | |||
606 | /* enable escape clock. */ | ||
607 | exynos_mipi_dsi_enable_byte_clock(dsim, 1); | ||
608 | |||
609 | /* enable byte clk and escape clock */ | ||
610 | exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); | ||
611 | /* escape clock on lane */ | ||
612 | exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, | ||
613 | (DSIM_LANE_CLOCK | dsim->data_lane), 1); | ||
614 | |||
615 | dev_dbg(dsim->dev, "byte clock is %luMHz\n", | ||
616 | (byte_clk / MHZ)); | ||
617 | dev_dbg(dsim->dev, "escape clock that user's need is %lu\n", | ||
618 | (dsim->dsim_config->esc_clk / MHZ)); | ||
619 | dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div); | ||
620 | dev_dbg(dsim->dev, "escape clock is %luMHz\n", | ||
621 | ((byte_clk / esc_div) / MHZ)); | ||
622 | |||
623 | if ((byte_clk / esc_div) > escape_clk) { | ||
624 | esc_clk_error_rate = escape_clk / | ||
625 | (byte_clk / esc_div); | ||
626 | dev_warn(dsim->dev, "error rate is %lu over.\n", | ||
627 | (esc_clk_error_rate / 100)); | ||
628 | } else if ((byte_clk / esc_div) < (escape_clk)) { | ||
629 | esc_clk_error_rate = (byte_clk / esc_div) / | ||
630 | escape_clk; | ||
631 | dev_warn(dsim->dev, "error rate is %lu under.\n", | ||
632 | (esc_clk_error_rate / 100)); | ||
633 | } | ||
634 | } else { | ||
635 | exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, | ||
636 | (DSIM_LANE_CLOCK | dsim->data_lane), 0); | ||
637 | exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); | ||
638 | |||
639 | /* disable escape clock. */ | ||
640 | exynos_mipi_dsi_enable_byte_clock(dsim, 0); | ||
641 | |||
642 | if (byte_clk_sel == DSIM_PLL_OUT_DIV8) | ||
643 | exynos_mipi_dsi_pll_on(dsim, 0); | ||
644 | } | ||
645 | |||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) | ||
650 | { | ||
651 | dsim->state = DSIM_STATE_INIT; | ||
652 | |||
653 | switch (dsim->dsim_config->e_no_data_lane) { | ||
654 | case DSIM_DATA_LANE_1: | ||
655 | dsim->data_lane = DSIM_LANE_DATA0; | ||
656 | break; | ||
657 | case DSIM_DATA_LANE_2: | ||
658 | dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; | ||
659 | break; | ||
660 | case DSIM_DATA_LANE_3: | ||
661 | dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | | ||
662 | DSIM_LANE_DATA2; | ||
663 | break; | ||
664 | case DSIM_DATA_LANE_4: | ||
665 | dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | | ||
666 | DSIM_LANE_DATA2 | DSIM_LANE_DATA3; | ||
667 | break; | ||
668 | default: | ||
669 | dev_info(dsim->dev, "data lane is invalid.\n"); | ||
670 | return -EINVAL; | ||
671 | } | ||
672 | |||
673 | exynos_mipi_dsi_sw_reset(dsim); | ||
674 | exynos_mipi_dsi_func_reset(dsim); | ||
675 | |||
676 | exynos_mipi_dsi_dp_dn_swap(dsim, 0); | ||
677 | |||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim) | ||
682 | { | ||
683 | unsigned int src = 0; | ||
684 | |||
685 | src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE); | ||
686 | exynos_mipi_dsi_set_interrupt(dsim, src, 1); | ||
687 | |||
688 | src = 0; | ||
689 | src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY); | ||
690 | exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1); | ||
691 | } | ||
692 | |||
693 | int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, | ||
694 | unsigned int enable) | ||
695 | { | ||
696 | /* enable only frame done interrupt */ | ||
697 | exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); | ||
698 | |||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, | ||
703 | unsigned int enable) | ||
704 | { | ||
705 | |||
706 | /* consider Main display and Sub display. */ | ||
707 | |||
708 | exynos_mipi_dsi_set_main_stand_by(dsim, enable); | ||
709 | } | ||
710 | |||
711 | int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, | ||
712 | struct mipi_dsim_config *dsim_config) | ||
713 | { | ||
714 | struct mipi_dsim_platform_data *dsim_pd; | ||
715 | struct fb_videomode *timing; | ||
716 | |||
717 | dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; | ||
718 | timing = (struct fb_videomode *)dsim_pd->lcd_panel_info; | ||
719 | |||
720 | /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ | ||
721 | if (dsim_config->e_interface == (u32) DSIM_VIDEO) { | ||
722 | if (dsim_config->auto_vertical_cnt == 0) { | ||
723 | exynos_mipi_dsi_set_main_disp_vporch(dsim, | ||
724 | dsim_config->cmd_allow, | ||
725 | timing->lower_margin, | ||
726 | timing->upper_margin); | ||
727 | exynos_mipi_dsi_set_main_disp_hporch(dsim, | ||
728 | timing->right_margin, | ||
729 | timing->left_margin); | ||
730 | exynos_mipi_dsi_set_main_disp_sync_area(dsim, | ||
731 | timing->vsync_len, | ||
732 | timing->hsync_len); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres, | ||
737 | timing->yres); | ||
738 | |||
739 | exynos_mipi_dsi_display_config(dsim, dsim_config); | ||
740 | |||
741 | dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n", | ||
742 | timing->xres, timing->yres); | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) | ||
748 | { | ||
749 | unsigned int time_out = 100; | ||
750 | |||
751 | switch (dsim->state) { | ||
752 | case DSIM_STATE_INIT: | ||
753 | exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); | ||
754 | |||
755 | /* dsi configuration */ | ||
756 | exynos_mipi_dsi_init_config(dsim); | ||
757 | exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); | ||
758 | exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); | ||
759 | |||
760 | /* set clock configuration */ | ||
761 | exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1); | ||
762 | |||
763 | /* check clock and data lane state are stop state */ | ||
764 | while (!(exynos_mipi_dsi_is_lane_state(dsim))) { | ||
765 | time_out--; | ||
766 | if (time_out == 0) { | ||
767 | dev_err(dsim->dev, | ||
768 | "DSI Master is not stop state.\n"); | ||
769 | dev_err(dsim->dev, | ||
770 | "Check initialization process\n"); | ||
771 | |||
772 | return -EINVAL; | ||
773 | } | ||
774 | } | ||
775 | if (time_out != 0) { | ||
776 | dev_info(dsim->dev, | ||
777 | "DSI Master driver has been completed.\n"); | ||
778 | dev_info(dsim->dev, "DSI Master state is stop state\n"); | ||
779 | } | ||
780 | |||
781 | dsim->state = DSIM_STATE_STOP; | ||
782 | |||
783 | /* BTA sequence counters */ | ||
784 | exynos_mipi_dsi_set_stop_state_counter(dsim, | ||
785 | dsim->dsim_config->stop_holding_cnt); | ||
786 | exynos_mipi_dsi_set_bta_timeout(dsim, | ||
787 | dsim->dsim_config->bta_timeout); | ||
788 | exynos_mipi_dsi_set_lpdr_timeout(dsim, | ||
789 | dsim->dsim_config->rx_timeout); | ||
790 | |||
791 | return 0; | ||
792 | default: | ||
793 | dev_info(dsim->dev, "DSI Master is already init.\n"); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) | ||
801 | { | ||
802 | if (dsim->state != DSIM_STATE_STOP) { | ||
803 | dev_warn(dsim->dev, "DSIM is not in stop state.\n"); | ||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) { | ||
808 | dev_warn(dsim->dev, "clock source is external bypass.\n"); | ||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | dsim->state = DSIM_STATE_HSCLKEN; | ||
813 | |||
814 | /* set LCDC and CPU transfer mode to HS. */ | ||
815 | exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); | ||
816 | exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); | ||
817 | exynos_mipi_dsi_enable_hs_clock(dsim, 1); | ||
818 | |||
819 | return 0; | ||
820 | } | ||
821 | |||
822 | int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, | ||
823 | unsigned int mode) | ||
824 | { | ||
825 | if (mode) { | ||
826 | if (dsim->state != DSIM_STATE_HSCLKEN) { | ||
827 | dev_err(dsim->dev, "HS Clock lane is not enabled.\n"); | ||
828 | return -EINVAL; | ||
829 | } | ||
830 | |||
831 | exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); | ||
832 | } else { | ||
833 | if (dsim->state == DSIM_STATE_INIT || dsim->state == | ||
834 | DSIM_STATE_ULPS) { | ||
835 | dev_err(dsim->dev, | ||
836 | "DSI Master is not STOP or HSDT state.\n"); | ||
837 | return -EINVAL; | ||
838 | } | ||
839 | |||
840 | exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); | ||
841 | } | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) | ||
847 | { | ||
848 | return _exynos_mipi_dsi_get_frame_done_status(dsim); | ||
849 | } | ||
850 | |||
851 | int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) | ||
852 | { | ||
853 | _exynos_mipi_dsi_clear_frame_done(dsim); | ||
854 | |||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, | ||
859 | unsigned int val) | ||
860 | { | ||
861 | int try = TRY_FIFO_CLEAR; | ||
862 | |||
863 | exynos_mipi_dsi_sw_reset_release(dsim); | ||
864 | exynos_mipi_dsi_func_reset(dsim); | ||
865 | |||
866 | do { | ||
867 | if (exynos_mipi_dsi_get_sw_reset_release(dsim)) { | ||
868 | exynos_mipi_dsi_init_interrupt(dsim); | ||
869 | dev_dbg(dsim->dev, "reset release done.\n"); | ||
870 | return 0; | ||
871 | } | ||
872 | } while (--try); | ||
873 | |||
874 | dev_err(dsim->dev, "failed to clear dsim fifo.\n"); | ||
875 | return -EAGAIN; | ||
876 | } | ||
877 | |||
878 | MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); | ||
879 | MODULE_DESCRIPTION("Samusung SoC MIPI-DSI common driver"); | ||
880 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h new file mode 100644 index 000000000000..412552274df3 --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* linux/drivers/video/exynos_mipi_dsi_common.h | ||
2 | * | ||
3 | * Header file for Samsung SoC MIPI-DSI common driver. | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae <inki.dae@samsung.com> | ||
8 | * Donghwa Lee <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef _EXYNOS_MIPI_DSI_COMMON_H | ||
16 | #define _EXYNOS_MIPI_DSI_COMMON_H | ||
17 | |||
18 | static DECLARE_COMPLETION(dsim_rd_comp); | ||
19 | static DECLARE_COMPLETION(dsim_wr_comp); | ||
20 | |||
21 | int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, | ||
22 | const unsigned char *data0, unsigned int data_size); | ||
23 | int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, | ||
24 | unsigned int data0, unsigned int req_size, u8 *rx_buf); | ||
25 | irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id); | ||
26 | void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim); | ||
27 | int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); | ||
28 | void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, | ||
29 | unsigned int enable); | ||
30 | int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, | ||
31 | struct mipi_dsim_config *dsim_info); | ||
32 | int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); | ||
33 | int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); | ||
34 | int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, | ||
35 | unsigned int mode); | ||
36 | int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, | ||
37 | unsigned int enable); | ||
38 | int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); | ||
39 | int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); | ||
40 | |||
41 | extern struct fb_info *registered_fb[FB_MAX] __read_mostly; | ||
42 | |||
43 | int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, | ||
44 | unsigned int val); | ||
45 | |||
46 | #endif /* _EXYNOS_MIPI_DSI_COMMON_H */ | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c new file mode 100644 index 000000000000..c148d06540c1 --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c | |||
@@ -0,0 +1,618 @@ | |||
1 | /* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c | ||
2 | * | ||
3 | * Samsung SoC MIPI-DSI lowlevel driver. | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae, <inki.dae@samsung.com> | ||
8 | * Donghwa Lee, <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/wait.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/ctype.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include <video/exynos_mipi_dsim.h> | ||
28 | |||
29 | #include "exynos_mipi_dsi_regs.h" | ||
30 | #include "exynos_mipi_dsi_lowlevel.h" | ||
31 | |||
32 | void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) | ||
33 | { | ||
34 | unsigned int reg; | ||
35 | |||
36 | reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); | ||
37 | |||
38 | reg |= DSIM_FUNCRST; | ||
39 | |||
40 | writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); | ||
41 | } | ||
42 | |||
43 | void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) | ||
44 | { | ||
45 | unsigned int reg; | ||
46 | |||
47 | reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); | ||
48 | |||
49 | reg |= DSIM_SWRST; | ||
50 | |||
51 | writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); | ||
52 | } | ||
53 | |||
54 | void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim) | ||
55 | { | ||
56 | unsigned int reg; | ||
57 | |||
58 | reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
59 | |||
60 | reg |= INTSRC_SW_RST_RELEASE; | ||
61 | |||
62 | writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
63 | } | ||
64 | |||
65 | int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim) | ||
66 | { | ||
67 | return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) & | ||
68 | INTSRC_SW_RST_RELEASE; | ||
69 | } | ||
70 | |||
71 | unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim) | ||
72 | { | ||
73 | unsigned int reg; | ||
74 | |||
75 | reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK); | ||
76 | |||
77 | return reg; | ||
78 | } | ||
79 | |||
80 | void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, | ||
81 | unsigned int mode, unsigned int mask) | ||
82 | { | ||
83 | unsigned int reg = 0; | ||
84 | |||
85 | if (mask) | ||
86 | reg |= mode; | ||
87 | else | ||
88 | reg &= ~mode; | ||
89 | |||
90 | writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK); | ||
91 | } | ||
92 | |||
93 | void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, | ||
94 | unsigned int cfg) | ||
95 | { | ||
96 | unsigned int reg; | ||
97 | |||
98 | reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); | ||
99 | |||
100 | writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); | ||
101 | mdelay(10); | ||
102 | reg |= cfg; | ||
103 | |||
104 | writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * this function set PLL P, M and S value in D-PHY | ||
109 | */ | ||
110 | void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, | ||
111 | unsigned int value) | ||
112 | { | ||
113 | writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR); | ||
114 | } | ||
115 | |||
116 | void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, | ||
117 | unsigned int enable) | ||
118 | { | ||
119 | unsigned int reg; | ||
120 | |||
121 | reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL); | ||
122 | |||
123 | reg &= ~DSIM_MAIN_STAND_BY; | ||
124 | |||
125 | if (enable) | ||
126 | reg |= DSIM_MAIN_STAND_BY; | ||
127 | |||
128 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); | ||
129 | } | ||
130 | |||
131 | void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, | ||
132 | unsigned int width_resol, unsigned int height_resol) | ||
133 | { | ||
134 | unsigned int reg; | ||
135 | |||
136 | /* standby should be set after configuration so set to not ready*/ | ||
137 | reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) & | ||
138 | ~(DSIM_MAIN_STAND_BY); | ||
139 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); | ||
140 | |||
141 | reg &= ~((0x7ff << 16) | (0x7ff << 0)); | ||
142 | reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); | ||
143 | |||
144 | reg |= DSIM_MAIN_STAND_BY; | ||
145 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); | ||
146 | } | ||
147 | |||
148 | void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, | ||
149 | unsigned int cmd_allow, unsigned int vfront, unsigned int vback) | ||
150 | { | ||
151 | unsigned int reg; | ||
152 | |||
153 | reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) & | ||
154 | ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | | ||
155 | (DSIM_MAIN_VBP_MASK)); | ||
156 | |||
157 | reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) | | ||
158 | DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) | | ||
159 | DSIM_MAIN_VBP_SHIFT(vback & 0x7ff)); | ||
160 | |||
161 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH); | ||
162 | } | ||
163 | |||
164 | void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, | ||
165 | unsigned int front, unsigned int back) | ||
166 | { | ||
167 | unsigned int reg; | ||
168 | |||
169 | reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) & | ||
170 | ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); | ||
171 | |||
172 | reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back); | ||
173 | |||
174 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH); | ||
175 | } | ||
176 | |||
177 | void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, | ||
178 | unsigned int vert, unsigned int hori) | ||
179 | { | ||
180 | unsigned int reg; | ||
181 | |||
182 | reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) & | ||
183 | ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); | ||
184 | |||
185 | reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) | | ||
186 | DSIM_MAIN_HSA_SHIFT(hori)); | ||
187 | |||
188 | writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC); | ||
189 | } | ||
190 | |||
191 | void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, | ||
192 | unsigned int vert, unsigned int hori) | ||
193 | { | ||
194 | unsigned int reg; | ||
195 | |||
196 | reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) & | ||
197 | ~(DSIM_SUB_STANDY_MASK); | ||
198 | |||
199 | writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); | ||
200 | |||
201 | reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); | ||
202 | reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) | | ||
203 | DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff)); | ||
204 | writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); | ||
205 | |||
206 | reg |= DSIM_SUB_STANDY_SHIFT(1); | ||
207 | writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); | ||
208 | } | ||
209 | |||
210 | void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) | ||
211 | { | ||
212 | struct mipi_dsim_config *dsim_config = dsim->dsim_config; | ||
213 | |||
214 | unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & | ||
215 | ~((1 << 28) | (0x1f << 20) | (0x3 << 5)); | ||
216 | |||
217 | cfg = ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) | | ||
218 | (DSIM_EOT_DISABLE(dsim_config->eot_disable)) | | ||
219 | (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) | | ||
220 | (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) | | ||
221 | (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) | | ||
222 | (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) | | ||
223 | (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) | | ||
224 | (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane))); | ||
225 | |||
226 | writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); | ||
227 | } | ||
228 | |||
229 | void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, | ||
230 | struct mipi_dsim_config *dsim_config) | ||
231 | { | ||
232 | u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & | ||
233 | ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) | | ||
234 | (0x3 << 16) | (0x7 << 8)); | ||
235 | |||
236 | if (dsim_config->e_interface == DSIM_VIDEO) | ||
237 | reg |= (1 << 25); | ||
238 | else if (dsim_config->e_interface == DSIM_COMMAND) | ||
239 | reg &= ~(1 << 25); | ||
240 | else { | ||
241 | dev_err(dsim->dev, "unknown lcd type.\n"); | ||
242 | return; | ||
243 | } | ||
244 | |||
245 | /* main lcd */ | ||
246 | reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 | | ||
247 | ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 | | ||
248 | ((u8) (dsim_config->e_pixel_format) & 0x7) << 12; | ||
249 | |||
250 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); | ||
251 | } | ||
252 | |||
253 | void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, | ||
254 | unsigned int enable) | ||
255 | { | ||
256 | unsigned int reg; | ||
257 | |||
258 | reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG); | ||
259 | |||
260 | if (enable) | ||
261 | reg |= DSIM_LANE_ENx(lane); | ||
262 | else | ||
263 | reg &= ~DSIM_LANE_ENx(lane); | ||
264 | |||
265 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); | ||
266 | } | ||
267 | |||
268 | |||
269 | void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, | ||
270 | unsigned int count) | ||
271 | { | ||
272 | unsigned int cfg; | ||
273 | |||
274 | /* get the data lane number. */ | ||
275 | cfg = DSIM_NUM_OF_DATALANE_SHIFT(count); | ||
276 | |||
277 | writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); | ||
278 | } | ||
279 | |||
280 | void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, | ||
281 | unsigned int afc_code) | ||
282 | { | ||
283 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR); | ||
284 | |||
285 | if (enable) { | ||
286 | reg |= (1 << 14); | ||
287 | reg &= ~(0x7 << 5); | ||
288 | reg |= (afc_code & 0x7) << 5; | ||
289 | } else | ||
290 | reg &= ~(1 << 14); | ||
291 | |||
292 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR); | ||
293 | } | ||
294 | |||
295 | void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, | ||
296 | unsigned int enable) | ||
297 | { | ||
298 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & | ||
299 | ~(DSIM_PLL_BYPASS_SHIFT(0x1)); | ||
300 | |||
301 | reg |= DSIM_PLL_BYPASS_SHIFT(enable); | ||
302 | |||
303 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
304 | } | ||
305 | |||
306 | void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, | ||
307 | unsigned int m, unsigned int s) | ||
308 | { | ||
309 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
310 | |||
311 | reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1); | ||
312 | |||
313 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
314 | } | ||
315 | |||
316 | void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, | ||
317 | unsigned int freq_band) | ||
318 | { | ||
319 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & | ||
320 | ~(DSIM_FREQ_BAND_SHIFT(0x1f)); | ||
321 | |||
322 | reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f); | ||
323 | |||
324 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
325 | } | ||
326 | |||
327 | void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, | ||
328 | unsigned int pre_divider, unsigned int main_divider, | ||
329 | unsigned int scaler) | ||
330 | { | ||
331 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & | ||
332 | ~(0x7ffff << 1); | ||
333 | |||
334 | reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 | | ||
335 | (scaler & 0x7) << 1; | ||
336 | |||
337 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
338 | } | ||
339 | |||
340 | void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, | ||
341 | unsigned int lock_time) | ||
342 | { | ||
343 | writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR); | ||
344 | } | ||
345 | |||
346 | void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable) | ||
347 | { | ||
348 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & | ||
349 | ~(DSIM_PLL_EN_SHIFT(0x1)); | ||
350 | |||
351 | reg |= DSIM_PLL_EN_SHIFT(enable & 0x1); | ||
352 | |||
353 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
354 | } | ||
355 | |||
356 | void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, | ||
357 | unsigned int src) | ||
358 | { | ||
359 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & | ||
360 | ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3)); | ||
361 | |||
362 | reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src)); | ||
363 | |||
364 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
365 | } | ||
366 | |||
367 | void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, | ||
368 | unsigned int enable) | ||
369 | { | ||
370 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & | ||
371 | ~(DSIM_BYTE_CLKEN_SHIFT(0x1)); | ||
372 | |||
373 | reg |= DSIM_BYTE_CLKEN_SHIFT(enable); | ||
374 | |||
375 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
376 | } | ||
377 | |||
378 | void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, | ||
379 | unsigned int enable, unsigned int prs_val) | ||
380 | { | ||
381 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & | ||
382 | ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff); | ||
383 | |||
384 | reg |= DSIM_ESC_CLKEN_SHIFT(enable); | ||
385 | if (enable) | ||
386 | reg |= prs_val; | ||
387 | |||
388 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
389 | } | ||
390 | |||
391 | void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, | ||
392 | unsigned int lane_sel, unsigned int enable) | ||
393 | { | ||
394 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
395 | |||
396 | if (enable) | ||
397 | reg |= DSIM_LANE_ESC_CLKEN(lane_sel); | ||
398 | else | ||
399 | |||
400 | reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); | ||
401 | |||
402 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
403 | } | ||
404 | |||
405 | void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, | ||
406 | unsigned int enable) | ||
407 | { | ||
408 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & | ||
409 | ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1)); | ||
410 | |||
411 | reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1)); | ||
412 | |||
413 | writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
414 | } | ||
415 | |||
416 | unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) | ||
417 | { | ||
418 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); | ||
419 | |||
420 | /** | ||
421 | * check clock and data lane states. | ||
422 | * if MIPI-DSI controller was enabled at bootloader then | ||
423 | * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. | ||
424 | * so it should be checked for two case. | ||
425 | */ | ||
426 | if ((reg & DSIM_STOP_STATE_DAT(0xf)) && | ||
427 | ((reg & DSIM_STOP_STATE_CLK) || | ||
428 | (reg & DSIM_TX_READY_HS_CLK))) | ||
429 | return 1; | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, | ||
435 | unsigned int cnt_val) | ||
436 | { | ||
437 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & | ||
438 | ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff)); | ||
439 | |||
440 | reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff)); | ||
441 | |||
442 | writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
443 | } | ||
444 | |||
445 | void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, | ||
446 | unsigned int timeout) | ||
447 | { | ||
448 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & | ||
449 | ~(DSIM_BTA_TOUT_SHIFT(0xff)); | ||
450 | |||
451 | reg |= (DSIM_BTA_TOUT_SHIFT(timeout)); | ||
452 | |||
453 | writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); | ||
454 | } | ||
455 | |||
456 | void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, | ||
457 | unsigned int timeout) | ||
458 | { | ||
459 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & | ||
460 | ~(DSIM_LPDR_TOUT_SHIFT(0xffff)); | ||
461 | |||
462 | reg |= (DSIM_LPDR_TOUT_SHIFT(timeout)); | ||
463 | |||
464 | writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); | ||
465 | } | ||
466 | |||
467 | void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, | ||
468 | unsigned int lp) | ||
469 | { | ||
470 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
471 | |||
472 | reg &= ~DSIM_CMD_LPDT_LP; | ||
473 | |||
474 | if (lp) | ||
475 | reg |= DSIM_CMD_LPDT_LP; | ||
476 | |||
477 | writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
478 | } | ||
479 | |||
480 | void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, | ||
481 | unsigned int lp) | ||
482 | { | ||
483 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
484 | |||
485 | reg &= ~DSIM_TX_LPDT_LP; | ||
486 | |||
487 | if (lp) | ||
488 | reg |= DSIM_TX_LPDT_LP; | ||
489 | |||
490 | writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); | ||
491 | } | ||
492 | |||
493 | void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, | ||
494 | unsigned int enable) | ||
495 | { | ||
496 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & | ||
497 | ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1)); | ||
498 | |||
499 | reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable); | ||
500 | |||
501 | writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); | ||
502 | } | ||
503 | |||
504 | void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, | ||
505 | unsigned int swap_en) | ||
506 | { | ||
507 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); | ||
508 | |||
509 | reg &= ~(0x3 << 0); | ||
510 | reg |= (swap_en & 0x3) << 0; | ||
511 | |||
512 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); | ||
513 | } | ||
514 | |||
515 | void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, | ||
516 | unsigned int hs_zero) | ||
517 | { | ||
518 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & | ||
519 | ~(0xf << 28); | ||
520 | |||
521 | reg |= ((hs_zero & 0xf) << 28); | ||
522 | |||
523 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
524 | } | ||
525 | |||
526 | void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) | ||
527 | { | ||
528 | unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & | ||
529 | ~(0x7 << 20); | ||
530 | |||
531 | reg |= ((prep & 0x7) << 20); | ||
532 | |||
533 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); | ||
534 | } | ||
535 | |||
536 | unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim) | ||
537 | { | ||
538 | return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
539 | } | ||
540 | |||
541 | void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, | ||
542 | unsigned int src) | ||
543 | { | ||
544 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
545 | |||
546 | reg |= src; | ||
547 | |||
548 | writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
549 | } | ||
550 | |||
551 | void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, | ||
552 | unsigned int src, unsigned int enable) | ||
553 | { | ||
554 | unsigned int reg = 0; | ||
555 | |||
556 | if (enable) | ||
557 | reg |= src; | ||
558 | else | ||
559 | reg &= ~src; | ||
560 | |||
561 | writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
562 | } | ||
563 | |||
564 | unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) | ||
565 | { | ||
566 | unsigned int reg; | ||
567 | |||
568 | reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); | ||
569 | |||
570 | return reg & (1 << 31) ? 1 : 0; | ||
571 | } | ||
572 | |||
573 | unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) | ||
574 | { | ||
575 | return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f); | ||
576 | } | ||
577 | |||
578 | void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, | ||
579 | unsigned int di, unsigned int data0, unsigned int data1) | ||
580 | { | ||
581 | unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0); | ||
582 | |||
583 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); | ||
584 | } | ||
585 | |||
586 | void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, | ||
587 | unsigned int di, unsigned int data0) | ||
588 | { | ||
589 | unsigned int reg = (data0 << 8) | (di << 0); | ||
590 | |||
591 | writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); | ||
592 | } | ||
593 | |||
594 | unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim) | ||
595 | { | ||
596 | return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO); | ||
597 | } | ||
598 | |||
599 | unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) | ||
600 | { | ||
601 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
602 | |||
603 | return (reg & INTSRC_FRAME_DONE) ? 1 : 0; | ||
604 | } | ||
605 | |||
606 | void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) | ||
607 | { | ||
608 | unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); | ||
609 | |||
610 | writel(reg | INTSRC_FRAME_DONE, dsim->reg_base + | ||
611 | EXYNOS_DSIM_INTSRC); | ||
612 | } | ||
613 | |||
614 | void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, | ||
615 | unsigned int tx_data) | ||
616 | { | ||
617 | writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD); | ||
618 | } | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h new file mode 100644 index 000000000000..85460701c7ea --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h | ||
2 | * | ||
3 | * Header file for Samsung SoC MIPI-DSI lowlevel driver. | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae <inki.dae@samsung.com> | ||
8 | * Donghwa Lee <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H | ||
16 | #define _EXYNOS_MIPI_DSI_LOWLEVEL_H | ||
17 | |||
18 | void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); | ||
19 | void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); | ||
20 | void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim); | ||
21 | int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim); | ||
22 | void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, | ||
23 | unsigned int mode, unsigned int mask); | ||
24 | void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, | ||
25 | unsigned int count); | ||
26 | void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, | ||
27 | unsigned int cfg); | ||
28 | void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, | ||
29 | unsigned int value); | ||
30 | void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, | ||
31 | unsigned int value); | ||
32 | void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, | ||
33 | unsigned int enable); | ||
34 | void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, | ||
35 | unsigned int width_resol, unsigned int height_resol); | ||
36 | void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, | ||
37 | unsigned int cmd_allow, unsigned int vfront, unsigned int vback); | ||
38 | void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, | ||
39 | unsigned int front, unsigned int back); | ||
40 | void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, | ||
41 | unsigned int vert, unsigned int hori); | ||
42 | void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, | ||
43 | unsigned int vert, unsigned int hori); | ||
44 | void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); | ||
45 | void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, | ||
46 | struct mipi_dsim_config *dsim_config); | ||
47 | void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, | ||
48 | unsigned int count); | ||
49 | void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, | ||
50 | unsigned int enable); | ||
51 | void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, | ||
52 | unsigned int afc_code); | ||
53 | void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, | ||
54 | unsigned int enable); | ||
55 | void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, | ||
56 | unsigned int m, unsigned int s); | ||
57 | void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, | ||
58 | unsigned int freq_band); | ||
59 | void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, | ||
60 | unsigned int pre_divider, unsigned int main_divider, | ||
61 | unsigned int scaler); | ||
62 | void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, | ||
63 | unsigned int lock_time); | ||
64 | void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, | ||
65 | unsigned int enable); | ||
66 | void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, | ||
67 | unsigned int src); | ||
68 | void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, | ||
69 | unsigned int enable); | ||
70 | void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, | ||
71 | unsigned int enable, unsigned int prs_val); | ||
72 | void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, | ||
73 | unsigned int lane_sel, unsigned int enable); | ||
74 | void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, | ||
75 | unsigned int enable); | ||
76 | unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); | ||
77 | void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, | ||
78 | unsigned int cnt_val); | ||
79 | void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, | ||
80 | unsigned int timeout); | ||
81 | void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, | ||
82 | unsigned int timeout); | ||
83 | void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, | ||
84 | unsigned int lp); | ||
85 | void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, | ||
86 | unsigned int lp); | ||
87 | void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, | ||
88 | unsigned int enable); | ||
89 | void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, | ||
90 | unsigned int swap_en); | ||
91 | void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, | ||
92 | unsigned int hs_zero); | ||
93 | void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep); | ||
94 | unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim); | ||
95 | unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim); | ||
96 | void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, | ||
97 | unsigned int src); | ||
98 | void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, | ||
99 | unsigned int src, unsigned int enable); | ||
100 | unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); | ||
101 | unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); | ||
102 | unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); | ||
103 | void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); | ||
104 | void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di, | ||
105 | unsigned int data0, unsigned int data1); | ||
106 | void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, | ||
107 | unsigned int tx_data); | ||
108 | void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, | ||
109 | unsigned int data0, unsigned int data1); | ||
110 | unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim); | ||
111 | |||
112 | #endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ | ||
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h new file mode 100644 index 000000000000..4227106d3fd0 --- /dev/null +++ b/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h | |||
@@ -0,0 +1,149 @@ | |||
1 | /* linux/driver/video/exynos/exynos_mipi_dsi_regs.h | ||
2 | * | ||
3 | * Register definition file for Samsung MIPI-DSIM driver | ||
4 | * | ||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd | ||
6 | * | ||
7 | * InKi Dae <inki.dae@samsung.com> | ||
8 | * Donghwa Lee <dh09.lee@samsung.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #ifndef _EXYNOS_MIPI_DSI_REGS_H | ||
16 | #define _EXYNOS_MIPI_DSI_REGS_H | ||
17 | |||
18 | #define EXYNOS_DSIM_STATUS 0x0 /* Status register */ | ||
19 | #define EXYNOS_DSIM_SWRST 0x4 /* Software reset register */ | ||
20 | #define EXYNOS_DSIM_CLKCTRL 0x8 /* Clock control register */ | ||
21 | #define EXYNOS_DSIM_TIMEOUT 0xc /* Time out register */ | ||
22 | #define EXYNOS_DSIM_CONFIG 0x10 /* Configuration register */ | ||
23 | #define EXYNOS_DSIM_ESCMODE 0x14 /* Escape mode register */ | ||
24 | |||
25 | /* Main display image resolution register */ | ||
26 | #define EXYNOS_DSIM_MDRESOL 0x18 | ||
27 | #define EXYNOS_DSIM_MVPORCH 0x1c /* Main display Vporch register */ | ||
28 | #define EXYNOS_DSIM_MHPORCH 0x20 /* Main display Hporch register */ | ||
29 | #define EXYNOS_DSIM_MSYNC 0x24 /* Main display sync area register */ | ||
30 | |||
31 | /* Sub display image resolution register */ | ||
32 | #define EXYNOS_DSIM_SDRESOL 0x28 | ||
33 | #define EXYNOS_DSIM_INTSRC 0x2c /* Interrupt source register */ | ||
34 | #define EXYNOS_DSIM_INTMSK 0x30 /* Interrupt mask register */ | ||
35 | #define EXYNOS_DSIM_PKTHDR 0x34 /* Packet Header FIFO register */ | ||
36 | #define EXYNOS_DSIM_PAYLOAD 0x38 /* Payload FIFO register */ | ||
37 | #define EXYNOS_DSIM_RXFIFO 0x3c /* Read FIFO register */ | ||
38 | #define EXYNOS_DSIM_FIFOTHLD 0x40 /* FIFO threshold level register */ | ||
39 | #define EXYNOS_DSIM_FIFOCTRL 0x44 /* FIFO status and control register */ | ||
40 | |||
41 | /* FIFO memory AC characteristic register */ | ||
42 | #define EXYNOS_DSIM_PLLCTRL 0x4c /* PLL control register */ | ||
43 | #define EXYNOS_DSIM_PLLTMR 0x50 /* PLL timer register */ | ||
44 | #define EXYNOS_DSIM_PHYACCHR 0x54 /* D-PHY AC characteristic register */ | ||
45 | #define EXYNOS_DSIM_PHYACCHR1 0x58 /* D-PHY AC characteristic register1 */ | ||
46 | |||
47 | /* DSIM_STATUS */ | ||
48 | #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) | ||
49 | #define DSIM_STOP_STATE_CLK (1 << 8) | ||
50 | #define DSIM_TX_READY_HS_CLK (1 << 10) | ||
51 | |||
52 | /* DSIM_SWRST */ | ||
53 | #define DSIM_FUNCRST (1 << 16) | ||
54 | #define DSIM_SWRST (1 << 0) | ||
55 | |||
56 | /* EXYNOS_DSIM_TIMEOUT */ | ||
57 | #define DSIM_LPDR_TOUT_SHIFT(x) ((x) << 0) | ||
58 | #define DSIM_BTA_TOUT_SHIFT(x) ((x) << 16) | ||
59 | |||
60 | /* EXYNOS_DSIM_CLKCTRL */ | ||
61 | #define DSIM_LANE_ESC_CLKEN(x) (((x) & 0x1f) << 19) | ||
62 | #define DSIM_BYTE_CLKEN_SHIFT(x) ((x) << 24) | ||
63 | #define DSIM_BYTE_CLK_SRC_SHIFT(x) ((x) << 25) | ||
64 | #define DSIM_PLL_BYPASS_SHIFT(x) ((x) << 27) | ||
65 | #define DSIM_ESC_CLKEN_SHIFT(x) ((x) << 28) | ||
66 | #define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31) | ||
67 | |||
68 | /* EXYNOS_DSIM_CONFIG */ | ||
69 | #define DSIM_LANE_ENx(x) (((x) & 0x1f) << 0) | ||
70 | #define DSIM_NUM_OF_DATALANE_SHIFT(x) ((x) << 5) | ||
71 | #define DSIM_HSA_MODE_SHIFT(x) ((x) << 20) | ||
72 | #define DSIM_HBP_MODE_SHIFT(x) ((x) << 21) | ||
73 | #define DSIM_HFP_MODE_SHIFT(x) ((x) << 22) | ||
74 | #define DSIM_HSE_MODE_SHIFT(x) ((x) << 23) | ||
75 | #define DSIM_AUTO_MODE_SHIFT(x) ((x) << 24) | ||
76 | #define DSIM_EOT_DISABLE(x) ((x) << 28) | ||
77 | #define DSIM_AUTO_FLUSH(x) ((x) << 29) | ||
78 | |||
79 | #define DSIM_NUM_OF_DATA_LANE(x) ((x) << DSIM_NUM_OF_DATALANE_SHIFT) | ||
80 | |||
81 | /* EXYNOS_DSIM_ESCMODE */ | ||
82 | #define DSIM_TX_LPDT_LP (1 << 6) | ||
83 | #define DSIM_CMD_LPDT_LP (1 << 7) | ||
84 | #define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20) | ||
85 | #define DSIM_STOP_STATE_CNT_SHIFT(x) ((x) << 21) | ||
86 | |||
87 | /* EXYNOS_DSIM_MDRESOL */ | ||
88 | #define DSIM_MAIN_STAND_BY (1 << 31) | ||
89 | #define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) | ||
90 | #define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) | ||
91 | |||
92 | /* EXYNOS_DSIM_MVPORCH */ | ||
93 | #define DSIM_CMD_ALLOW_SHIFT(x) ((x) << 28) | ||
94 | #define DSIM_STABLE_VFP_SHIFT(x) ((x) << 16) | ||
95 | #define DSIM_MAIN_VBP_SHIFT(x) ((x) << 0) | ||
96 | #define DSIM_CMD_ALLOW_MASK (0xf << 28) | ||
97 | #define DSIM_STABLE_VFP_MASK (0x7ff << 16) | ||
98 | #define DSIM_MAIN_VBP_MASK (0x7ff << 0) | ||
99 | |||
100 | /* EXYNOS_DSIM_MHPORCH */ | ||
101 | #define DSIM_MAIN_HFP_SHIFT(x) ((x) << 16) | ||
102 | #define DSIM_MAIN_HBP_SHIFT(x) ((x) << 0) | ||
103 | #define DSIM_MAIN_HFP_MASK ((0xffff) << 16) | ||
104 | #define DSIM_MAIN_HBP_MASK ((0xffff) << 0) | ||
105 | |||
106 | /* EXYNOS_DSIM_MSYNC */ | ||
107 | #define DSIM_MAIN_VSA_SHIFT(x) ((x) << 22) | ||
108 | #define DSIM_MAIN_HSA_SHIFT(x) ((x) << 0) | ||
109 | #define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) | ||
110 | #define DSIM_MAIN_HSA_MASK ((0xffff) << 0) | ||
111 | |||
112 | /* EXYNOS_DSIM_SDRESOL */ | ||
113 | #define DSIM_SUB_STANDY_SHIFT(x) ((x) << 31) | ||
114 | #define DSIM_SUB_VRESOL_SHIFT(x) ((x) << 16) | ||
115 | #define DSIM_SUB_HRESOL_SHIFT(x) ((x) << 0) | ||
116 | #define DSIM_SUB_STANDY_MASK ((0x1) << 31) | ||
117 | #define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) | ||
118 | #define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) | ||
119 | |||
120 | /* EXYNOS_DSIM_INTSRC */ | ||
121 | #define INTSRC_PLL_STABLE (1 << 31) | ||
122 | #define INTSRC_SW_RST_RELEASE (1 << 30) | ||
123 | #define INTSRC_SFR_FIFO_EMPTY (1 << 29) | ||
124 | #define INTSRC_FRAME_DONE (1 << 24) | ||
125 | #define INTSRC_RX_DATA_DONE (1 << 18) | ||
126 | |||
127 | /* EXYNOS_DSIM_INTMSK */ | ||
128 | #define INTMSK_FIFO_EMPTY (1 << 29) | ||
129 | #define INTMSK_BTA (1 << 25) | ||
130 | #define INTMSK_FRAME_DONE (1 << 24) | ||
131 | #define INTMSK_RX_TIMEOUT (1 << 21) | ||
132 | #define INTMSK_BTA_TIMEOUT (1 << 20) | ||
133 | #define INTMSK_RX_DONE (1 << 18) | ||
134 | #define INTMSK_RX_TE (1 << 17) | ||
135 | #define INTMSK_RX_ACK (1 << 16) | ||
136 | #define INTMSK_RX_ECC_ERR (1 << 15) | ||
137 | #define INTMSK_RX_CRC_ERR (1 << 14) | ||
138 | |||
139 | /* EXYNOS_DSIM_FIFOCTRL */ | ||
140 | #define SFR_HEADER_EMPTY (1 << 22) | ||
141 | |||
142 | /* EXYNOS_DSIM_PHYACCHR */ | ||
143 | #define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) | ||
144 | |||
145 | /* EXYNOS_DSIM_PLLCTRL */ | ||
146 | #define DSIM_PLL_EN_SHIFT(x) ((x) << 23) | ||
147 | #define DSIM_FREQ_BAND_SHIFT(x) ((x) << 24) | ||
148 | |||
149 | #endif /* _EXYNOS_MIPI_DSI_REGS_H */ | ||
diff --git a/drivers/video/fbdev/exynos/s6e8ax0.c b/drivers/video/fbdev/exynos/s6e8ax0.c new file mode 100644 index 000000000000..29e70ed3f154 --- /dev/null +++ b/drivers/video/fbdev/exynos/s6e8ax0.c | |||
@@ -0,0 +1,898 @@ | |||
1 | /* linux/drivers/video/exynos/s6e8ax0.c | ||
2 | * | ||
3 | * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver. | ||
4 | * | ||
5 | * Inki Dae, <inki.dae@samsung.com> | ||
6 | * Donghwa Lee, <dh09.lee@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/mutex.h> | ||
17 | #include <linux/wait.h> | ||
18 | #include <linux/ctype.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/irq.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/lcd.h> | ||
24 | #include <linux/fb.h> | ||
25 | #include <linux/backlight.h> | ||
26 | #include <linux/regulator/consumer.h> | ||
27 | |||
28 | #include <video/mipi_display.h> | ||
29 | #include <video/exynos_mipi_dsim.h> | ||
30 | |||
31 | #define LDI_MTP_LENGTH 24 | ||
32 | #define DSIM_PM_STABLE_TIME 10 | ||
33 | #define MIN_BRIGHTNESS 0 | ||
34 | #define MAX_BRIGHTNESS 24 | ||
35 | #define GAMMA_TABLE_COUNT 26 | ||
36 | |||
37 | #define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) | ||
38 | #define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) | ||
39 | #define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) | ||
40 | |||
41 | #define lcd_to_master(a) (a->dsim_dev->master) | ||
42 | #define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) | ||
43 | |||
44 | enum { | ||
45 | DSIM_NONE_STATE = 0, | ||
46 | DSIM_RESUME_COMPLETE = 1, | ||
47 | DSIM_FRAME_DONE = 2, | ||
48 | }; | ||
49 | |||
50 | struct s6e8ax0 { | ||
51 | struct device *dev; | ||
52 | unsigned int power; | ||
53 | unsigned int id; | ||
54 | unsigned int gamma; | ||
55 | unsigned int acl_enable; | ||
56 | unsigned int cur_acl; | ||
57 | |||
58 | struct lcd_device *ld; | ||
59 | struct backlight_device *bd; | ||
60 | |||
61 | struct mipi_dsim_lcd_device *dsim_dev; | ||
62 | struct lcd_platform_data *ddi_pd; | ||
63 | struct mutex lock; | ||
64 | bool enabled; | ||
65 | }; | ||
66 | |||
67 | |||
68 | static struct regulator_bulk_data supplies[] = { | ||
69 | { .supply = "vdd3", }, | ||
70 | { .supply = "vci", }, | ||
71 | }; | ||
72 | |||
73 | static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd) | ||
74 | { | ||
75 | int ret = 0; | ||
76 | struct lcd_platform_data *pd = NULL; | ||
77 | |||
78 | pd = lcd->ddi_pd; | ||
79 | mutex_lock(&lcd->lock); | ||
80 | if (!lcd->enabled) { | ||
81 | ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); | ||
82 | if (ret) | ||
83 | goto out; | ||
84 | |||
85 | lcd->enabled = true; | ||
86 | } | ||
87 | msleep(pd->power_on_delay); | ||
88 | out: | ||
89 | mutex_unlock(&lcd->lock); | ||
90 | } | ||
91 | |||
92 | static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd) | ||
93 | { | ||
94 | int ret = 0; | ||
95 | |||
96 | mutex_lock(&lcd->lock); | ||
97 | if (lcd->enabled) { | ||
98 | ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); | ||
99 | if (ret) | ||
100 | goto out; | ||
101 | |||
102 | lcd->enabled = false; | ||
103 | } | ||
104 | out: | ||
105 | mutex_unlock(&lcd->lock); | ||
106 | } | ||
107 | |||
108 | static const unsigned char s6e8ax0_22_gamma_30[] = { | ||
109 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf, | ||
110 | 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0, | ||
111 | 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, | ||
112 | }; | ||
113 | |||
114 | static const unsigned char s6e8ax0_22_gamma_50[] = { | ||
115 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0, | ||
116 | 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb, | ||
117 | 0x00, 0x70, 0x00, 0x68, 0x00, 0x86, | ||
118 | }; | ||
119 | |||
120 | static const unsigned char s6e8ax0_22_gamma_60[] = { | ||
121 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4, | ||
122 | 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba, | ||
123 | 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d, | ||
124 | }; | ||
125 | |||
126 | static const unsigned char s6e8ax0_22_gamma_70[] = { | ||
127 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8, | ||
128 | 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9, | ||
129 | 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93, | ||
130 | }; | ||
131 | |||
132 | static const unsigned char s6e8ax0_22_gamma_80[] = { | ||
133 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9, | ||
134 | 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb, | ||
135 | 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99, | ||
136 | }; | ||
137 | |||
138 | static const unsigned char s6e8ax0_22_gamma_90[] = { | ||
139 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc, | ||
140 | 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9, | ||
141 | 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e, | ||
142 | }; | ||
143 | |||
144 | static const unsigned char s6e8ax0_22_gamma_100[] = { | ||
145 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce, | ||
146 | 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6, | ||
147 | 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5, | ||
148 | }; | ||
149 | |||
150 | static const unsigned char s6e8ax0_22_gamma_120[] = { | ||
151 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf, | ||
152 | 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6, | ||
153 | 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac, | ||
154 | }; | ||
155 | |||
156 | static const unsigned char s6e8ax0_22_gamma_130[] = { | ||
157 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0, | ||
158 | 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4, | ||
159 | 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1, | ||
160 | }; | ||
161 | |||
162 | static const unsigned char s6e8ax0_22_gamma_140[] = { | ||
163 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0, | ||
164 | 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4, | ||
165 | 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5, | ||
166 | }; | ||
167 | |||
168 | static const unsigned char s6e8ax0_22_gamma_150[] = { | ||
169 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0, | ||
170 | 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1, | ||
171 | 0x00, 0x99, 0x00, 0x90, 0x00, 0xba, | ||
172 | }; | ||
173 | |||
174 | static const unsigned char s6e8ax0_22_gamma_160[] = { | ||
175 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0, | ||
176 | 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1, | ||
177 | 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe, | ||
178 | }; | ||
179 | |||
180 | static const unsigned char s6e8ax0_22_gamma_170[] = { | ||
181 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1, | ||
182 | 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1, | ||
183 | 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2, | ||
184 | }; | ||
185 | |||
186 | static const unsigned char s6e8ax0_22_gamma_180[] = { | ||
187 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2, | ||
188 | 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1, | ||
189 | 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5, | ||
190 | }; | ||
191 | |||
192 | static const unsigned char s6e8ax0_22_gamma_190[] = { | ||
193 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2, | ||
194 | 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf, | ||
195 | 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9, | ||
196 | }; | ||
197 | |||
198 | static const unsigned char s6e8ax0_22_gamma_200[] = { | ||
199 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2, | ||
200 | 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae, | ||
201 | 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd, | ||
202 | }; | ||
203 | |||
204 | static const unsigned char s6e8ax0_22_gamma_210[] = { | ||
205 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1, | ||
206 | 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad, | ||
207 | 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4, | ||
208 | }; | ||
209 | |||
210 | static const unsigned char s6e8ax0_22_gamma_220[] = { | ||
211 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1, | ||
212 | 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad, | ||
213 | 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3, | ||
214 | }; | ||
215 | |||
216 | static const unsigned char s6e8ax0_22_gamma_230[] = { | ||
217 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1, | ||
218 | 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad, | ||
219 | 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7, | ||
220 | }; | ||
221 | |||
222 | static const unsigned char s6e8ax0_22_gamma_240[] = { | ||
223 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2, | ||
224 | 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab, | ||
225 | 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb, | ||
226 | }; | ||
227 | |||
228 | static const unsigned char s6e8ax0_22_gamma_250[] = { | ||
229 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2, | ||
230 | 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab, | ||
231 | 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde, | ||
232 | }; | ||
233 | |||
234 | static const unsigned char s6e8ax0_22_gamma_260[] = { | ||
235 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1, | ||
236 | 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac, | ||
237 | 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0, | ||
238 | }; | ||
239 | |||
240 | static const unsigned char s6e8ax0_22_gamma_270[] = { | ||
241 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2, | ||
242 | 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa, | ||
243 | 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4, | ||
244 | }; | ||
245 | |||
246 | static const unsigned char s6e8ax0_22_gamma_280[] = { | ||
247 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0, | ||
248 | 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9, | ||
249 | 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7, | ||
250 | }; | ||
251 | |||
252 | static const unsigned char s6e8ax0_22_gamma_300[] = { | ||
253 | 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2, | ||
254 | 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, | ||
255 | 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, | ||
256 | }; | ||
257 | |||
258 | static const unsigned char *s6e8ax0_22_gamma_table[] = { | ||
259 | s6e8ax0_22_gamma_30, | ||
260 | s6e8ax0_22_gamma_50, | ||
261 | s6e8ax0_22_gamma_60, | ||
262 | s6e8ax0_22_gamma_70, | ||
263 | s6e8ax0_22_gamma_80, | ||
264 | s6e8ax0_22_gamma_90, | ||
265 | s6e8ax0_22_gamma_100, | ||
266 | s6e8ax0_22_gamma_120, | ||
267 | s6e8ax0_22_gamma_130, | ||
268 | s6e8ax0_22_gamma_140, | ||
269 | s6e8ax0_22_gamma_150, | ||
270 | s6e8ax0_22_gamma_160, | ||
271 | s6e8ax0_22_gamma_170, | ||
272 | s6e8ax0_22_gamma_180, | ||
273 | s6e8ax0_22_gamma_190, | ||
274 | s6e8ax0_22_gamma_200, | ||
275 | s6e8ax0_22_gamma_210, | ||
276 | s6e8ax0_22_gamma_220, | ||
277 | s6e8ax0_22_gamma_230, | ||
278 | s6e8ax0_22_gamma_240, | ||
279 | s6e8ax0_22_gamma_250, | ||
280 | s6e8ax0_22_gamma_260, | ||
281 | s6e8ax0_22_gamma_270, | ||
282 | s6e8ax0_22_gamma_280, | ||
283 | s6e8ax0_22_gamma_300, | ||
284 | }; | ||
285 | |||
286 | static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) | ||
287 | { | ||
288 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
289 | |||
290 | static const unsigned char data_to_send[] = { | ||
291 | 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, | ||
292 | 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, | ||
293 | 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, | ||
294 | 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 | ||
295 | }; | ||
296 | static const unsigned char data_to_send_panel_reverse[] = { | ||
297 | 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, | ||
298 | 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, | ||
299 | 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, | ||
300 | 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 | ||
301 | }; | ||
302 | |||
303 | if (lcd->dsim_dev->panel_reverse) | ||
304 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
305 | data_to_send_panel_reverse, | ||
306 | ARRAY_SIZE(data_to_send_panel_reverse)); | ||
307 | else | ||
308 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
309 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
310 | } | ||
311 | |||
312 | static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) | ||
313 | { | ||
314 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
315 | static const unsigned char data_to_send[] = { | ||
316 | 0xf2, 0x80, 0x03, 0x0d | ||
317 | }; | ||
318 | |||
319 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
320 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
321 | } | ||
322 | |||
323 | /* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */ | ||
324 | static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd) | ||
325 | { | ||
326 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
327 | unsigned int gamma = lcd->bd->props.brightness; | ||
328 | |||
329 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
330 | s6e8ax0_22_gamma_table[gamma], | ||
331 | GAMMA_TABLE_COUNT); | ||
332 | } | ||
333 | |||
334 | static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) | ||
335 | { | ||
336 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
337 | static const unsigned char data_to_send[] = { | ||
338 | 0xf7, 0x03 | ||
339 | }; | ||
340 | |||
341 | ops->cmd_write(lcd_to_master(lcd), | ||
342 | MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, | ||
343 | ARRAY_SIZE(data_to_send)); | ||
344 | } | ||
345 | |||
346 | static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd) | ||
347 | { | ||
348 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
349 | static const unsigned char data_to_send[] = { | ||
350 | 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40, | ||
351 | 0x0d, 0x00, 0x00 | ||
352 | }; | ||
353 | |||
354 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
355 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
356 | } | ||
357 | |||
358 | static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd) | ||
359 | { | ||
360 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
361 | static const unsigned char data_to_send[] = { | ||
362 | 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, | ||
363 | 0x00 | ||
364 | }; | ||
365 | |||
366 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
367 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
368 | } | ||
369 | |||
370 | static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd) | ||
371 | { | ||
372 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
373 | static const unsigned char data_to_send[] = { | ||
374 | 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d | ||
375 | }; | ||
376 | |||
377 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
378 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
379 | } | ||
380 | |||
381 | static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd) | ||
382 | { | ||
383 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
384 | static const unsigned char data_to_send[] = { | ||
385 | 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 | ||
386 | }; | ||
387 | |||
388 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
389 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
390 | } | ||
391 | |||
392 | static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd) | ||
393 | { | ||
394 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
395 | static const unsigned char data_to_send[] = { | ||
396 | 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 | ||
397 | }; | ||
398 | |||
399 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
400 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
401 | } | ||
402 | static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd) | ||
403 | { | ||
404 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
405 | static const unsigned char data_to_send[] = { | ||
406 | 0xe3, 0x40 | ||
407 | }; | ||
408 | |||
409 | ops->cmd_write(lcd_to_master(lcd), | ||
410 | MIPI_DSI_DCS_SHORT_WRITE_PARAM, | ||
411 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
412 | } | ||
413 | |||
414 | static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd) | ||
415 | { | ||
416 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
417 | static const unsigned char data_to_send[] = { | ||
418 | 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 | ||
419 | }; | ||
420 | |||
421 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
422 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
423 | } | ||
424 | |||
425 | static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd) | ||
426 | { | ||
427 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
428 | static const unsigned char data_to_send[] = { | ||
429 | 0xb1, 0x04, 0x00 | ||
430 | }; | ||
431 | |||
432 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
433 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
434 | } | ||
435 | |||
436 | static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd) | ||
437 | { | ||
438 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
439 | static const unsigned char data_to_send[] = { | ||
440 | 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11, | ||
441 | 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed, | ||
442 | 0x64, 0xaf | ||
443 | }; | ||
444 | |||
445 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
446 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
447 | } | ||
448 | |||
449 | static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) | ||
450 | { | ||
451 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
452 | static const unsigned char data_to_send[] = { | ||
453 | 0x10, 0x00 | ||
454 | }; | ||
455 | |||
456 | ops->cmd_write(lcd_to_master(lcd), | ||
457 | MIPI_DSI_DCS_SHORT_WRITE, | ||
458 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
459 | } | ||
460 | |||
461 | static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) | ||
462 | { | ||
463 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
464 | static const unsigned char data_to_send[] = { | ||
465 | 0x11, 0x00 | ||
466 | }; | ||
467 | |||
468 | ops->cmd_write(lcd_to_master(lcd), | ||
469 | MIPI_DSI_DCS_SHORT_WRITE, | ||
470 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
471 | } | ||
472 | |||
473 | static void s6e8ax0_display_on(struct s6e8ax0 *lcd) | ||
474 | { | ||
475 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
476 | static const unsigned char data_to_send[] = { | ||
477 | 0x29, 0x00 | ||
478 | }; | ||
479 | |||
480 | ops->cmd_write(lcd_to_master(lcd), | ||
481 | MIPI_DSI_DCS_SHORT_WRITE, | ||
482 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
483 | } | ||
484 | |||
485 | static void s6e8ax0_display_off(struct s6e8ax0 *lcd) | ||
486 | { | ||
487 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
488 | static const unsigned char data_to_send[] = { | ||
489 | 0x28, 0x00 | ||
490 | }; | ||
491 | |||
492 | ops->cmd_write(lcd_to_master(lcd), | ||
493 | MIPI_DSI_DCS_SHORT_WRITE, | ||
494 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
495 | } | ||
496 | |||
497 | static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd) | ||
498 | { | ||
499 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
500 | static const unsigned char data_to_send[] = { | ||
501 | 0xf0, 0x5a, 0x5a | ||
502 | }; | ||
503 | |||
504 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
505 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
506 | } | ||
507 | |||
508 | static void s6e8ax0_acl_on(struct s6e8ax0 *lcd) | ||
509 | { | ||
510 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
511 | static const unsigned char data_to_send[] = { | ||
512 | 0xc0, 0x01 | ||
513 | }; | ||
514 | |||
515 | ops->cmd_write(lcd_to_master(lcd), | ||
516 | MIPI_DSI_DCS_SHORT_WRITE, | ||
517 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
518 | } | ||
519 | |||
520 | static void s6e8ax0_acl_off(struct s6e8ax0 *lcd) | ||
521 | { | ||
522 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
523 | static const unsigned char data_to_send[] = { | ||
524 | 0xc0, 0x00 | ||
525 | }; | ||
526 | |||
527 | ops->cmd_write(lcd_to_master(lcd), | ||
528 | MIPI_DSI_DCS_SHORT_WRITE, | ||
529 | data_to_send, ARRAY_SIZE(data_to_send)); | ||
530 | } | ||
531 | |||
532 | /* Full white 50% reducing setting */ | ||
533 | static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd) | ||
534 | { | ||
535 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
536 | /* Full white 50% reducing setting */ | ||
537 | static const unsigned char cutoff_50[] = { | ||
538 | 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, | ||
539 | 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
540 | 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, | ||
541 | 0x3f, 0x46 | ||
542 | }; | ||
543 | /* Full white 45% reducing setting */ | ||
544 | static const unsigned char cutoff_45[] = { | ||
545 | 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, | ||
546 | 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
547 | 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, | ||
548 | 0x37, 0x3d | ||
549 | }; | ||
550 | /* Full white 40% reducing setting */ | ||
551 | static const unsigned char cutoff_40[] = { | ||
552 | 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, | ||
553 | 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
554 | 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, | ||
555 | 0x31, 0x36 | ||
556 | }; | ||
557 | |||
558 | if (lcd->acl_enable) { | ||
559 | if (lcd->cur_acl == 0) { | ||
560 | if (lcd->gamma == 0 || lcd->gamma == 1) { | ||
561 | s6e8ax0_acl_off(lcd); | ||
562 | dev_dbg(&lcd->ld->dev, | ||
563 | "cur_acl=%d\n", lcd->cur_acl); | ||
564 | } else | ||
565 | s6e8ax0_acl_on(lcd); | ||
566 | } | ||
567 | switch (lcd->gamma) { | ||
568 | case 0: /* 30cd */ | ||
569 | s6e8ax0_acl_off(lcd); | ||
570 | lcd->cur_acl = 0; | ||
571 | break; | ||
572 | case 1 ... 3: /* 50cd ~ 90cd */ | ||
573 | ops->cmd_write(lcd_to_master(lcd), | ||
574 | MIPI_DSI_DCS_LONG_WRITE, | ||
575 | cutoff_40, | ||
576 | ARRAY_SIZE(cutoff_40)); | ||
577 | lcd->cur_acl = 40; | ||
578 | break; | ||
579 | case 4 ... 7: /* 120cd ~ 210cd */ | ||
580 | ops->cmd_write(lcd_to_master(lcd), | ||
581 | MIPI_DSI_DCS_LONG_WRITE, | ||
582 | cutoff_45, | ||
583 | ARRAY_SIZE(cutoff_45)); | ||
584 | lcd->cur_acl = 45; | ||
585 | break; | ||
586 | case 8 ... 10: /* 220cd ~ 300cd */ | ||
587 | ops->cmd_write(lcd_to_master(lcd), | ||
588 | MIPI_DSI_DCS_LONG_WRITE, | ||
589 | cutoff_50, | ||
590 | ARRAY_SIZE(cutoff_50)); | ||
591 | lcd->cur_acl = 50; | ||
592 | break; | ||
593 | default: | ||
594 | break; | ||
595 | } | ||
596 | } else { | ||
597 | s6e8ax0_acl_off(lcd); | ||
598 | lcd->cur_acl = 0; | ||
599 | dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); | ||
600 | } | ||
601 | } | ||
602 | |||
603 | static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) | ||
604 | { | ||
605 | unsigned int ret; | ||
606 | unsigned int addr = 0xd1; /* MTP ID */ | ||
607 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
608 | |||
609 | ret = ops->cmd_read(lcd_to_master(lcd), | ||
610 | MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, | ||
611 | addr, 3, mtp_id); | ||
612 | } | ||
613 | |||
614 | static int s6e8ax0_panel_init(struct s6e8ax0 *lcd) | ||
615 | { | ||
616 | s6e8ax0_apply_level2_key(lcd); | ||
617 | s6e8ax0_sleep_out(lcd); | ||
618 | msleep(1); | ||
619 | s6e8ax0_panel_cond(lcd); | ||
620 | s6e8ax0_display_cond(lcd); | ||
621 | s6e8ax0_gamma_cond(lcd); | ||
622 | s6e8ax0_gamma_update(lcd); | ||
623 | |||
624 | s6e8ax0_etc_cond1(lcd); | ||
625 | s6e8ax0_etc_cond2(lcd); | ||
626 | s6e8ax0_etc_cond3(lcd); | ||
627 | s6e8ax0_etc_cond4(lcd); | ||
628 | s6e8ax0_etc_cond5(lcd); | ||
629 | s6e8ax0_etc_cond6(lcd); | ||
630 | s6e8ax0_etc_cond7(lcd); | ||
631 | |||
632 | s6e8ax0_elvss_nvm_set(lcd); | ||
633 | s6e8ax0_elvss_set(lcd); | ||
634 | |||
635 | s6e8ax0_acl_ctrl_set(lcd); | ||
636 | s6e8ax0_acl_on(lcd); | ||
637 | |||
638 | /* if ID3 value is not 33h, branch private elvss mode */ | ||
639 | msleep(lcd->ddi_pd->power_on_delay); | ||
640 | |||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness) | ||
645 | { | ||
646 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
647 | |||
648 | ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, | ||
649 | s6e8ax0_22_gamma_table[brightness], | ||
650 | ARRAY_SIZE(s6e8ax0_22_gamma_table)); | ||
651 | |||
652 | /* update gamma table. */ | ||
653 | s6e8ax0_gamma_update(lcd); | ||
654 | lcd->gamma = brightness; | ||
655 | |||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma) | ||
660 | { | ||
661 | s6e8ax0_update_gamma_ctrl(lcd, gamma); | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int s6e8ax0_set_power(struct lcd_device *ld, int power) | ||
667 | { | ||
668 | struct s6e8ax0 *lcd = lcd_get_data(ld); | ||
669 | struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); | ||
670 | int ret = 0; | ||
671 | |||
672 | if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && | ||
673 | power != FB_BLANK_NORMAL) { | ||
674 | dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); | ||
675 | return -EINVAL; | ||
676 | } | ||
677 | |||
678 | if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) { | ||
679 | /* LCD power on */ | ||
680 | if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power)) | ||
681 | || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) { | ||
682 | ret = ops->set_blank_mode(lcd_to_master(lcd), power); | ||
683 | if (!ret && lcd->power != power) | ||
684 | lcd->power = power; | ||
685 | } | ||
686 | } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) { | ||
687 | /* LCD power off */ | ||
688 | if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) || | ||
689 | (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { | ||
690 | ret = ops->set_early_blank_mode(lcd_to_master(lcd), | ||
691 | power); | ||
692 | if (!ret && lcd->power != power) | ||
693 | lcd->power = power; | ||
694 | } | ||
695 | } | ||
696 | |||
697 | return ret; | ||
698 | } | ||
699 | |||
700 | static int s6e8ax0_get_power(struct lcd_device *ld) | ||
701 | { | ||
702 | struct s6e8ax0 *lcd = lcd_get_data(ld); | ||
703 | |||
704 | return lcd->power; | ||
705 | } | ||
706 | |||
707 | static int s6e8ax0_get_brightness(struct backlight_device *bd) | ||
708 | { | ||
709 | return bd->props.brightness; | ||
710 | } | ||
711 | |||
712 | static int s6e8ax0_set_brightness(struct backlight_device *bd) | ||
713 | { | ||
714 | int ret = 0, brightness = bd->props.brightness; | ||
715 | struct s6e8ax0 *lcd = bl_get_data(bd); | ||
716 | |||
717 | if (brightness < MIN_BRIGHTNESS || | ||
718 | brightness > bd->props.max_brightness) { | ||
719 | dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", | ||
720 | MIN_BRIGHTNESS, MAX_BRIGHTNESS); | ||
721 | return -EINVAL; | ||
722 | } | ||
723 | |||
724 | ret = s6e8ax0_gamma_ctrl(lcd, brightness); | ||
725 | if (ret) { | ||
726 | dev_err(&bd->dev, "lcd brightness setting failed.\n"); | ||
727 | return -EIO; | ||
728 | } | ||
729 | |||
730 | return ret; | ||
731 | } | ||
732 | |||
733 | static struct lcd_ops s6e8ax0_lcd_ops = { | ||
734 | .set_power = s6e8ax0_set_power, | ||
735 | .get_power = s6e8ax0_get_power, | ||
736 | }; | ||
737 | |||
738 | static const struct backlight_ops s6e8ax0_backlight_ops = { | ||
739 | .get_brightness = s6e8ax0_get_brightness, | ||
740 | .update_status = s6e8ax0_set_brightness, | ||
741 | }; | ||
742 | |||
743 | static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) | ||
744 | { | ||
745 | struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); | ||
746 | |||
747 | msleep(lcd->ddi_pd->power_on_delay); | ||
748 | |||
749 | /* lcd power on */ | ||
750 | if (power) | ||
751 | s6e8ax0_regulator_enable(lcd); | ||
752 | else | ||
753 | s6e8ax0_regulator_disable(lcd); | ||
754 | |||
755 | msleep(lcd->ddi_pd->reset_delay); | ||
756 | |||
757 | /* lcd reset */ | ||
758 | if (lcd->ddi_pd->reset) | ||
759 | lcd->ddi_pd->reset(lcd->ld); | ||
760 | msleep(5); | ||
761 | } | ||
762 | |||
763 | static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) | ||
764 | { | ||
765 | struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); | ||
766 | |||
767 | s6e8ax0_panel_init(lcd); | ||
768 | s6e8ax0_display_on(lcd); | ||
769 | |||
770 | lcd->power = FB_BLANK_UNBLANK; | ||
771 | } | ||
772 | |||
773 | static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) | ||
774 | { | ||
775 | struct s6e8ax0 *lcd; | ||
776 | int ret; | ||
777 | u8 mtp_id[3] = {0, }; | ||
778 | |||
779 | lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL); | ||
780 | if (!lcd) { | ||
781 | dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); | ||
782 | return -ENOMEM; | ||
783 | } | ||
784 | |||
785 | lcd->dsim_dev = dsim_dev; | ||
786 | lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; | ||
787 | lcd->dev = &dsim_dev->dev; | ||
788 | |||
789 | mutex_init(&lcd->lock); | ||
790 | |||
791 | ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); | ||
792 | if (ret) { | ||
793 | dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); | ||
794 | return ret; | ||
795 | } | ||
796 | |||
797 | lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd, | ||
798 | &s6e8ax0_lcd_ops); | ||
799 | if (IS_ERR(lcd->ld)) { | ||
800 | dev_err(lcd->dev, "failed to register lcd ops.\n"); | ||
801 | return PTR_ERR(lcd->ld); | ||
802 | } | ||
803 | |||
804 | lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl", | ||
805 | lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL); | ||
806 | if (IS_ERR(lcd->bd)) { | ||
807 | dev_err(lcd->dev, "failed to register backlight ops.\n"); | ||
808 | return PTR_ERR(lcd->bd); | ||
809 | } | ||
810 | |||
811 | lcd->bd->props.max_brightness = MAX_BRIGHTNESS; | ||
812 | lcd->bd->props.brightness = MAX_BRIGHTNESS; | ||
813 | |||
814 | s6e8ax0_read_id(lcd, mtp_id); | ||
815 | if (mtp_id[0] == 0x00) | ||
816 | dev_err(lcd->dev, "read id failed\n"); | ||
817 | |||
818 | dev_info(lcd->dev, "Read ID : %x, %x, %x\n", | ||
819 | mtp_id[0], mtp_id[1], mtp_id[2]); | ||
820 | |||
821 | if (mtp_id[2] == 0x33) | ||
822 | dev_info(lcd->dev, | ||
823 | "ID-3 is 0xff does not support dynamic elvss\n"); | ||
824 | else | ||
825 | dev_info(lcd->dev, | ||
826 | "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); | ||
827 | |||
828 | lcd->acl_enable = 1; | ||
829 | lcd->cur_acl = 0; | ||
830 | |||
831 | dev_set_drvdata(&dsim_dev->dev, lcd); | ||
832 | |||
833 | dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n"); | ||
834 | |||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | #ifdef CONFIG_PM | ||
839 | static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) | ||
840 | { | ||
841 | struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); | ||
842 | |||
843 | s6e8ax0_sleep_in(lcd); | ||
844 | msleep(lcd->ddi_pd->power_off_delay); | ||
845 | s6e8ax0_display_off(lcd); | ||
846 | |||
847 | s6e8ax0_regulator_disable(lcd); | ||
848 | |||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) | ||
853 | { | ||
854 | struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); | ||
855 | |||
856 | s6e8ax0_sleep_out(lcd); | ||
857 | msleep(lcd->ddi_pd->power_on_delay); | ||
858 | |||
859 | s6e8ax0_regulator_enable(lcd); | ||
860 | s6e8ax0_set_sequence(dsim_dev); | ||
861 | |||
862 | return 0; | ||
863 | } | ||
864 | #else | ||
865 | #define s6e8ax0_suspend NULL | ||
866 | #define s6e8ax0_resume NULL | ||
867 | #endif | ||
868 | |||
869 | static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { | ||
870 | .name = "s6e8ax0", | ||
871 | .id = -1, | ||
872 | |||
873 | .power_on = s6e8ax0_power_on, | ||
874 | .set_sequence = s6e8ax0_set_sequence, | ||
875 | .probe = s6e8ax0_probe, | ||
876 | .suspend = s6e8ax0_suspend, | ||
877 | .resume = s6e8ax0_resume, | ||
878 | }; | ||
879 | |||
880 | static int s6e8ax0_init(void) | ||
881 | { | ||
882 | exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); | ||
883 | |||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | static void s6e8ax0_exit(void) | ||
888 | { | ||
889 | return; | ||
890 | } | ||
891 | |||
892 | module_init(s6e8ax0_init); | ||
893 | module_exit(s6e8ax0_exit); | ||
894 | |||
895 | MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); | ||
896 | MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); | ||
897 | MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver"); | ||
898 | MODULE_LICENSE("GPL"); | ||