blob: 190e941cf25d0869edd877403891d245cf4ac125 [file] [log] [blame]
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +00001/*
2 * Renesas SH-mobile MIPI DSI support
3 *
4 * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5 *
6 * This is free software; you can redistribute it and/or modify
7 * it under the terms of version 2 of the GNU General Public License as
8 * published by the Free Software Foundation.
9 */
10
Kuninori Morimoto26c3d7a2011-11-08 20:34:43 -080011#include <linux/bitmap.h>
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000012#include <linux/clk.h>
13#include <linux/delay.h>
14#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/platform_device.h>
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +000017#include <linux/pm_runtime.h>
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000018#include <linux/slab.h>
19#include <linux/string.h>
20#include <linux/types.h>
Paul Gortmaker355b2002011-07-03 16:17:28 -040021#include <linux/module.h>
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000022
23#include <video/mipi_display.h>
24#include <video/sh_mipi_dsi.h>
25#include <video/sh_mobile_lcdc.h>
26
Magnus Damm71b146c2010-11-17 06:44:25 +000027#define SYSCTRL 0x0000
28#define SYSCONF 0x0004
29#define TIMSET 0x0008
30#define RESREQSET0 0x0018
31#define RESREQSET1 0x001c
32#define HSTTOVSET 0x0020
33#define LPRTOVSET 0x0024
34#define TATOVSET 0x0028
35#define PRTOVSET 0x002c
36#define DSICTRL 0x0030
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000037#define DSIINTE 0x0060
Magnus Damm71b146c2010-11-17 06:44:25 +000038#define PHYCTRL 0x0070
39
Magnus Dammdeaba192010-11-17 09:53:25 +000040/* relative to linkbase */
41#define DTCTR 0x0000
42#define VMCTR1 0x0020
43#define VMCTR2 0x0024
44#define VMLEN1 0x0028
Kuninori Morimoto08750612011-11-08 20:35:05 -080045#define VMLEN2 0x002c
Magnus Dammdeaba192010-11-17 09:53:25 +000046#define CMTSRTREQ 0x0070
47#define CMTSRTCTR 0x00d0
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000048
49/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
50#define MAX_SH_MIPI_DSI 2
51
52struct sh_mipi {
53 void __iomem *base;
Magnus Dammdeaba192010-11-17 09:53:25 +000054 void __iomem *linkbase;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000055 struct clk *dsit_clk;
56 struct clk *dsip_clk;
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +000057 struct device *dev;
58
59 void *next_board_data;
60 void (*next_display_on)(void *board_data, struct fb_info *info);
61 void (*next_display_off)(void *board_data);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000062};
63
64static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
65
66/* Protect the above array */
67static DEFINE_MUTEX(array_lock);
68
69static struct sh_mipi *sh_mipi_by_handle(int handle)
70{
71 if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
72 return NULL;
73
74 return mipi_dsi[handle];
75}
76
77static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
78 u8 cmd, u8 param)
79{
80 u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
81 int cnt = 100;
82
83 /* transmit a short packet to LCD panel */
Magnus Dammdeaba192010-11-17 09:53:25 +000084 iowrite32(1 | data, mipi->linkbase + CMTSRTCTR);
85 iowrite32(1, mipi->linkbase + CMTSRTREQ);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000086
Magnus Dammdeaba192010-11-17 09:53:25 +000087 while ((ioread32(mipi->linkbase + CMTSRTREQ) & 1) && --cnt)
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +000088 udelay(1);
89
90 return cnt ? 0 : -ETIMEDOUT;
91}
92
93#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
94 -EINVAL : (c) - 1)
95
96static int sh_mipi_dcs(int handle, u8 cmd)
97{
98 struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
99 if (!mipi)
100 return -ENODEV;
101 return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
102}
103
104static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
105{
106 struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
107 if (!mipi)
108 return -ENODEV;
109 return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
110 param);
111}
112
113static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
114{
115 /*
116 * enable LCDC data tx, transition to LPS after completion of each HS
117 * packet
118 */
Magnus Dammdeaba192010-11-17 09:53:25 +0000119 iowrite32(0x00000002 | enable, mipi->linkbase + DTCTR);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000120}
121
122static void sh_mipi_shutdown(struct platform_device *pdev)
123{
124 struct sh_mipi *mipi = platform_get_drvdata(pdev);
125
126 sh_mipi_dsi_enable(mipi, false);
127}
128
Guennadi Liakhovetskic2439392010-07-21 10:13:17 +0000129static void mipi_display_on(void *arg, struct fb_info *info)
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000130{
131 struct sh_mipi *mipi = arg;
132
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000133 pm_runtime_get_sync(mipi->dev);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000134 sh_mipi_dsi_enable(mipi, true);
Magnus Damm6722a402010-11-17 06:44:54 +0000135
136 if (mipi->next_display_on)
137 mipi->next_display_on(mipi->next_board_data, info);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000138}
139
140static void mipi_display_off(void *arg)
141{
142 struct sh_mipi *mipi = arg;
143
Magnus Damm6722a402010-11-17 06:44:54 +0000144 if (mipi->next_display_off)
145 mipi->next_display_off(mipi->next_board_data);
146
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000147 sh_mipi_dsi_enable(mipi, false);
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000148 pm_runtime_put(mipi->dev);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000149}
150
151static int __init sh_mipi_setup(struct sh_mipi *mipi,
152 struct sh_mipi_dsi_info *pdata)
153{
154 void __iomem *base = mipi->base;
155 struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
Kuninori Morimotof8329062011-11-08 20:34:55 -0800156 u32 pctype, datatype, pixfmt, linelength, vmctr2;
Kuninori Morimoto08750612011-11-08 20:35:05 -0800157 u32 tmp, top, bottom, delay;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000158 bool yuv;
Kuninori Morimoto08750612011-11-08 20:35:05 -0800159 int bpp;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000160
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000161 /*
162 * Select data format. MIPI DSI is not hot-pluggable, so, we just use
163 * the default videomode. If this ever becomes a problem, We'll have to
164 * move this to mipi_display_on() above and use info->var.xres
165 */
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000166 switch (pdata->data_format) {
167 case MIPI_RGB888:
168 pctype = 0;
169 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
170 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000171 linelength = ch->lcd_cfg[0].xres * 3;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000172 yuv = false;
173 break;
174 case MIPI_RGB565:
175 pctype = 1;
176 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
177 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000178 linelength = ch->lcd_cfg[0].xres * 2;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000179 yuv = false;
180 break;
181 case MIPI_RGB666_LP:
182 pctype = 2;
183 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
184 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000185 linelength = ch->lcd_cfg[0].xres * 3;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000186 yuv = false;
187 break;
188 case MIPI_RGB666:
189 pctype = 3;
190 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
191 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000192 linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000193 yuv = false;
194 break;
195 case MIPI_BGR888:
196 pctype = 8;
197 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
198 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000199 linelength = ch->lcd_cfg[0].xres * 3;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000200 yuv = false;
201 break;
202 case MIPI_BGR565:
203 pctype = 9;
204 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
205 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000206 linelength = ch->lcd_cfg[0].xres * 2;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000207 yuv = false;
208 break;
209 case MIPI_BGR666_LP:
210 pctype = 0xa;
211 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
212 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000213 linelength = ch->lcd_cfg[0].xres * 3;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000214 yuv = false;
215 break;
216 case MIPI_BGR666:
217 pctype = 0xb;
218 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
219 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000220 linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000221 yuv = false;
222 break;
223 case MIPI_YUYV:
224 pctype = 4;
225 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
226 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000227 linelength = ch->lcd_cfg[0].xres * 2;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000228 yuv = true;
229 break;
230 case MIPI_UYVY:
231 pctype = 5;
232 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
233 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000234 linelength = ch->lcd_cfg[0].xres * 2;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000235 yuv = true;
236 break;
237 case MIPI_YUV420_L:
238 pctype = 6;
239 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
240 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000241 linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000242 yuv = true;
243 break;
244 case MIPI_YUV420:
245 pctype = 7;
246 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
247 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
248 /* Length of U/V line */
Guennadi Liakhovetski44432402010-09-03 07:20:04 +0000249 linelength = (ch->lcd_cfg[0].xres + 1) / 2;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000250 yuv = true;
251 break;
252 default:
253 return -EINVAL;
254 }
255
256 if ((yuv && ch->interface_type != YUV422) ||
257 (!yuv && ch->interface_type != RGB24))
258 return -EINVAL;
259
Kuninori Morimoto26c3d7a2011-11-08 20:34:43 -0800260 if (!pdata->lane)
261 return -EINVAL;
262
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000263 /* reset DSI link */
Magnus Damm71b146c2010-11-17 06:44:25 +0000264 iowrite32(0x00000001, base + SYSCTRL);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000265 /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
266 udelay(50);
Magnus Damm71b146c2010-11-17 06:44:25 +0000267 iowrite32(0x00000000, base + SYSCTRL);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000268
269 /* setup DSI link */
270
271 /*
272 * Default = ULPS enable |
273 * Contention detection enabled |
274 * EoT packet transmission enable |
275 * CRC check enable |
276 * ECC check enable
277 * additionally enable first two lanes
278 */
Kuninori Morimoto26c3d7a2011-11-08 20:34:43 -0800279 bitmap_fill((unsigned long *)&tmp, pdata->lane);
280 tmp |= 0x00003700;
281 iowrite32(tmp, base + SYSCONF);
282
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000283 /*
284 * T_wakeup = 0x7000
285 * T_hs-trail = 3
286 * T_hs-prepare = 3
287 * T_clk-trail = 3
288 * T_clk-prepare = 2
289 */
Magnus Damm71b146c2010-11-17 06:44:25 +0000290 iowrite32(0x70003332, base + TIMSET);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000291 /* no responses requested */
Magnus Damm71b146c2010-11-17 06:44:25 +0000292 iowrite32(0x00000000, base + RESREQSET0);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000293 /* request response to packets of type 0x28 */
Magnus Damm71b146c2010-11-17 06:44:25 +0000294 iowrite32(0x00000100, base + RESREQSET1);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000295 /* High-speed transmission timeout, default 0xffffffff */
Magnus Damm71b146c2010-11-17 06:44:25 +0000296 iowrite32(0x0fffffff, base + HSTTOVSET);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000297 /* LP reception timeout, default 0xffffffff */
Magnus Damm71b146c2010-11-17 06:44:25 +0000298 iowrite32(0x0fffffff, base + LPRTOVSET);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000299 /* Turn-around timeout, default 0xffffffff */
Magnus Damm71b146c2010-11-17 06:44:25 +0000300 iowrite32(0x0fffffff, base + TATOVSET);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000301 /* Peripheral reset timeout, default 0xffffffff */
Magnus Damm71b146c2010-11-17 06:44:25 +0000302 iowrite32(0x0fffffff, base + PRTOVSET);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000303 /* Enable timeout counters */
Magnus Damm71b146c2010-11-17 06:44:25 +0000304 iowrite32(0x00000f00, base + DSICTRL);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000305 /* Interrupts not used, disable all */
306 iowrite32(0, base + DSIINTE);
307 /* DSI-Tx bias on */
Magnus Damm71b146c2010-11-17 06:44:25 +0000308 iowrite32(0x00000001, base + PHYCTRL);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000309 udelay(200);
310 /* Deassert resets, power on, set multiplier */
Magnus Damm71b146c2010-11-17 06:44:25 +0000311 iowrite32(0x03070b01, base + PHYCTRL);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000312
313 /* setup l-bridge */
314
315 /*
316 * Enable transmission of all packets,
317 * transmit LPS after each HS packet completion
318 */
Magnus Dammdeaba192010-11-17 09:53:25 +0000319 iowrite32(0x00000006, mipi->linkbase + DTCTR);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000320 /* VSYNC width = 2 (<< 17) */
Guennadi Liakhovetski14bbb7c2010-12-29 08:12:29 +0000321 iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) |
322 (pdata->clksrc << 16) | (pctype << 12) | datatype,
Magnus Dammdeaba192010-11-17 09:53:25 +0000323 mipi->linkbase + VMCTR1);
Guennadi Liakhovetski14bbb7c2010-12-29 08:12:29 +0000324
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000325 /*
326 * Non-burst mode with sync pulses: VSE and HSE are output,
327 * HSA period allowed, no commands in LP
328 */
Kuninori Morimotof8329062011-11-08 20:34:55 -0800329 vmctr2 = 0;
330 if (pdata->flags & SH_MIPI_DSI_VSEE)
331 vmctr2 |= 1 << 23;
332 if (pdata->flags & SH_MIPI_DSI_HSEE)
333 vmctr2 |= 1 << 22;
334 if (pdata->flags & SH_MIPI_DSI_HSAE)
335 vmctr2 |= 1 << 21;
Kuninori Morimotod07a9d22011-11-08 20:34:33 -0800336 if (pdata->flags & SH_MIPI_DSI_BL2E)
337 vmctr2 |= 1 << 17;
Guennadi Liakhovetski14bbb7c2010-12-29 08:12:29 +0000338 if (pdata->flags & SH_MIPI_DSI_HSABM)
Kuninori Morimoto3c2a6592011-11-08 20:34:12 -0800339 vmctr2 |= 1 << 5;
Kuninori Morimoto32ba95c2011-11-08 20:34:01 -0800340 if (pdata->flags & SH_MIPI_DSI_HBPBM)
Kuninori Morimoto3c2a6592011-11-08 20:34:12 -0800341 vmctr2 |= 1 << 4;
Kuninori Morimotof7b0af62011-11-08 20:34:24 -0800342 if (pdata->flags & SH_MIPI_DSI_HFPBM)
343 vmctr2 |= 1 << 3;
Guennadi Liakhovetski14bbb7c2010-12-29 08:12:29 +0000344 iowrite32(vmctr2, mipi->linkbase + VMCTR2);
345
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000346 /*
Kuninori Morimoto08750612011-11-08 20:35:05 -0800347 * VMLEN1 = RGBLEN | HSALEN
348 *
349 * see
350 * Video mode - Blanking Packet setting
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000351 */
Kuninori Morimoto08750612011-11-08 20:35:05 -0800352 top = linelength << 16; /* RGBLEN */
353 bottom = 0x00000001;
354 if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */
355 bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10;
356 iowrite32(top | bottom , mipi->linkbase + VMLEN1);
357
358 /*
359 * VMLEN2 = HBPLEN | HFPLEN
360 *
361 * see
362 * Video mode - Blanking Packet setting
363 */
364 top = 0x00010000;
365 bottom = 0x00000001;
366 delay = 0;
367
368 if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */
369 top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin;
370 top = ((pdata->lane * top) - 10) << 16;
371 }
372 if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
373 bottom = ch->lcd_cfg[0].right_margin;
374 bottom = (pdata->lane * bottom) - 12;
375 }
376
377 bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */
378 if (pdata->lane > bpp) {
379 tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */
380 tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */
381 delay = (pdata->lane * tmp);
382 }
383
384 iowrite32(top | (bottom + delay) , mipi->linkbase + VMLEN2);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000385
386 msleep(5);
387
388 /* setup LCD panel */
389
390 /* cf. drivers/video/omap/lcd_mipid.c */
391 sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
392 msleep(120);
393 /*
394 * [7] - Page Address Mode
395 * [6] - Column Address Mode
396 * [5] - Page / Column Address Mode
397 * [4] - Display Device Line Refresh Order
398 * [3] - RGB/BGR Order
399 * [2] - Display Data Latch Data Order
400 * [1] - Flip Horizontal
401 * [0] - Flip Vertical
402 */
403 sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
404 /* cf. set_data_lines() */
405 sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
406 pixfmt << 4);
407 sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
408
409 return 0;
410}
411
412static int __init sh_mipi_probe(struct platform_device *pdev)
413{
414 struct sh_mipi *mipi;
415 struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
416 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Magnus Dammdeaba192010-11-17 09:53:25 +0000417 struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000418 unsigned long rate, f_current;
419 int idx = pdev->id, ret;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000420
Magnus Dammdeaba192010-11-17 09:53:25 +0000421 if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000422 return -ENODEV;
423
424 mutex_lock(&array_lock);
425 if (idx < 0)
426 for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
427 ;
428
429 if (idx == ARRAY_SIZE(mipi_dsi)) {
430 ret = -EBUSY;
431 goto efindslot;
432 }
433
434 mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
435 if (!mipi) {
436 ret = -ENOMEM;
437 goto ealloc;
438 }
439
440 if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
441 dev_err(&pdev->dev, "MIPI register region already claimed\n");
442 ret = -EBUSY;
443 goto ereqreg;
444 }
445
446 mipi->base = ioremap(res->start, resource_size(res));
447 if (!mipi->base) {
448 ret = -ENOMEM;
449 goto emap;
450 }
451
Magnus Dammdeaba192010-11-17 09:53:25 +0000452 if (!request_mem_region(res2->start, resource_size(res2), pdev->name)) {
453 dev_err(&pdev->dev, "MIPI register region 2 already claimed\n");
454 ret = -EBUSY;
455 goto ereqreg2;
456 }
457
458 mipi->linkbase = ioremap(res2->start, resource_size(res2));
459 if (!mipi->linkbase) {
460 ret = -ENOMEM;
461 goto emap2;
462 }
463
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000464 mipi->dev = &pdev->dev;
465
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000466 mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
467 if (IS_ERR(mipi->dsit_clk)) {
468 ret = PTR_ERR(mipi->dsit_clk);
469 goto eclktget;
470 }
471
472 f_current = clk_get_rate(mipi->dsit_clk);
473 /* 80MHz required by the datasheet */
474 rate = clk_round_rate(mipi->dsit_clk, 80000000);
475 if (rate > 0 && rate != f_current)
476 ret = clk_set_rate(mipi->dsit_clk, rate);
477 else
478 ret = rate;
479 if (ret < 0)
480 goto esettrate;
481
482 dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
483
Kuninori Morimoto92507412011-11-08 20:33:47 -0800484 mipi->dsip_clk = clk_get(&pdev->dev, "dsip_clk");
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000485 if (IS_ERR(mipi->dsip_clk)) {
486 ret = PTR_ERR(mipi->dsip_clk);
487 goto eclkpget;
488 }
489
490 f_current = clk_get_rate(mipi->dsip_clk);
491 /* Between 10 and 50MHz */
492 rate = clk_round_rate(mipi->dsip_clk, 24000000);
493 if (rate > 0 && rate != f_current)
494 ret = clk_set_rate(mipi->dsip_clk, rate);
495 else
496 ret = rate;
497 if (ret < 0)
498 goto esetprate;
499
500 dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
501
502 msleep(10);
503
504 ret = clk_enable(mipi->dsit_clk);
505 if (ret < 0)
506 goto eclkton;
507
508 ret = clk_enable(mipi->dsip_clk);
509 if (ret < 0)
510 goto eclkpon;
511
512 mipi_dsi[idx] = mipi;
513
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000514 pm_runtime_enable(&pdev->dev);
515 pm_runtime_resume(&pdev->dev);
516
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000517 ret = sh_mipi_setup(mipi, pdata);
518 if (ret < 0)
519 goto emipisetup;
520
521 mutex_unlock(&array_lock);
522 platform_set_drvdata(pdev, mipi);
523
Magnus Damm6722a402010-11-17 06:44:54 +0000524 /* Save original LCDC callbacks */
525 mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data;
526 mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on;
527 mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off;
528
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000529 /* Set up LCDC callbacks */
530 pdata->lcd_chan->board_cfg.board_data = mipi;
531 pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
532 pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000533 pdata->lcd_chan->board_cfg.owner = THIS_MODULE;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000534
535 return 0;
536
537emipisetup:
538 mipi_dsi[idx] = NULL;
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000539 pm_runtime_disable(&pdev->dev);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000540 clk_disable(mipi->dsip_clk);
541eclkpon:
542 clk_disable(mipi->dsit_clk);
543eclkton:
544esetprate:
545 clk_put(mipi->dsip_clk);
546eclkpget:
547esettrate:
548 clk_put(mipi->dsit_clk);
549eclktget:
Magnus Dammdeaba192010-11-17 09:53:25 +0000550 iounmap(mipi->linkbase);
551emap2:
552 release_mem_region(res2->start, resource_size(res2));
553ereqreg2:
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000554 iounmap(mipi->base);
555emap:
556 release_mem_region(res->start, resource_size(res));
557ereqreg:
558 kfree(mipi);
559ealloc:
560efindslot:
561 mutex_unlock(&array_lock);
562
563 return ret;
564}
565
566static int __exit sh_mipi_remove(struct platform_device *pdev)
567{
568 struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
569 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Magnus Dammdeaba192010-11-17 09:53:25 +0000570 struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000571 struct sh_mipi *mipi = platform_get_drvdata(pdev);
572 int i, ret;
573
574 mutex_lock(&array_lock);
575
576 for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
577 ;
578
579 if (i == ARRAY_SIZE(mipi_dsi)) {
580 ret = -EINVAL;
581 } else {
582 ret = 0;
583 mipi_dsi[i] = NULL;
584 }
585
586 mutex_unlock(&array_lock);
587
588 if (ret < 0)
589 return ret;
590
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000591 pdata->lcd_chan->board_cfg.owner = NULL;
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000592 pdata->lcd_chan->board_cfg.display_on = NULL;
593 pdata->lcd_chan->board_cfg.display_off = NULL;
594 pdata->lcd_chan->board_cfg.board_data = NULL;
595
Guennadi Liakhovetski236782a2010-12-27 10:23:05 +0000596 pm_runtime_disable(&pdev->dev);
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000597 clk_disable(mipi->dsip_clk);
598 clk_disable(mipi->dsit_clk);
599 clk_put(mipi->dsit_clk);
600 clk_put(mipi->dsip_clk);
Magnus Dammdeaba192010-11-17 09:53:25 +0000601 iounmap(mipi->linkbase);
602 if (res2)
603 release_mem_region(res2->start, resource_size(res2));
Guennadi Liakhovetski9fd04fe2010-05-23 14:00:43 +0000604 iounmap(mipi->base);
605 if (res)
606 release_mem_region(res->start, resource_size(res));
607 platform_set_drvdata(pdev, NULL);
608 kfree(mipi);
609
610 return 0;
611}
612
613static struct platform_driver sh_mipi_driver = {
614 .remove = __exit_p(sh_mipi_remove),
615 .shutdown = sh_mipi_shutdown,
616 .driver = {
617 .name = "sh-mipi-dsi",
618 },
619};
620
621static int __init sh_mipi_init(void)
622{
623 return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
624}
625module_init(sh_mipi_init);
626
627static void __exit sh_mipi_exit(void)
628{
629 platform_driver_unregister(&sh_mipi_driver);
630}
631module_exit(sh_mipi_exit);
632
633MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
634MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
635MODULE_LICENSE("GPL v2");