diff options
author | Tejun Heo <tj@kernel.org> | 2014-04-25 18:25:55 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2014-04-25 18:25:55 -0400 |
commit | 50bce01b0ee34ab9f18a2d5a7467053dda355d30 (patch) | |
tree | 148739c6b91abdeef3ec8d26b02132e21fb98a6f /drivers/video/fbdev/sh_mipi_dsi.c | |
parent | f8f22e53a262ebee37fc98004f16b066cf5bc125 (diff) | |
parent | 86d56134f1b67d0c18025ba5cade95c048ed528d (diff) |
Merge branch 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core into for-3.16
Pull in driver-core-next to receive kernfs_notify() updates which will
be used by the planned "cgroup.populated" implementation.
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/video/fbdev/sh_mipi_dsi.c')
-rw-r--r-- | drivers/video/fbdev/sh_mipi_dsi.c | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/drivers/video/fbdev/sh_mipi_dsi.c b/drivers/video/fbdev/sh_mipi_dsi.c new file mode 100644 index 000000000000..8f6e8ff620d4 --- /dev/null +++ b/drivers/video/fbdev/sh_mipi_dsi.c | |||
@@ -0,0 +1,587 @@ | |||
1 | /* | ||
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 | |||
11 | #include <linux/bitmap.h> | ||
12 | #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> | ||
17 | #include <linux/pm_runtime.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/module.h> | ||
22 | |||
23 | #include <video/mipi_display.h> | ||
24 | #include <video/sh_mipi_dsi.h> | ||
25 | #include <video/sh_mobile_lcdc.h> | ||
26 | |||
27 | #include "sh_mobile_lcdcfb.h" | ||
28 | |||
29 | #define SYSCTRL 0x0000 | ||
30 | #define SYSCONF 0x0004 | ||
31 | #define TIMSET 0x0008 | ||
32 | #define RESREQSET0 0x0018 | ||
33 | #define RESREQSET1 0x001c | ||
34 | #define HSTTOVSET 0x0020 | ||
35 | #define LPRTOVSET 0x0024 | ||
36 | #define TATOVSET 0x0028 | ||
37 | #define PRTOVSET 0x002c | ||
38 | #define DSICTRL 0x0030 | ||
39 | #define DSIINTE 0x0060 | ||
40 | #define PHYCTRL 0x0070 | ||
41 | |||
42 | /* relative to linkbase */ | ||
43 | #define DTCTR 0x0000 | ||
44 | #define VMCTR1 0x0020 | ||
45 | #define VMCTR2 0x0024 | ||
46 | #define VMLEN1 0x0028 | ||
47 | #define VMLEN2 0x002c | ||
48 | #define CMTSRTREQ 0x0070 | ||
49 | #define CMTSRTCTR 0x00d0 | ||
50 | |||
51 | /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ | ||
52 | #define MAX_SH_MIPI_DSI 2 | ||
53 | |||
54 | struct sh_mipi { | ||
55 | struct sh_mobile_lcdc_entity entity; | ||
56 | |||
57 | void __iomem *base; | ||
58 | void __iomem *linkbase; | ||
59 | struct clk *dsit_clk; | ||
60 | struct platform_device *pdev; | ||
61 | }; | ||
62 | |||
63 | #define to_sh_mipi(e) container_of(e, struct sh_mipi, entity) | ||
64 | |||
65 | static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; | ||
66 | |||
67 | /* Protect the above array */ | ||
68 | static DEFINE_MUTEX(array_lock); | ||
69 | |||
70 | static struct sh_mipi *sh_mipi_by_handle(int handle) | ||
71 | { | ||
72 | if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0) | ||
73 | return NULL; | ||
74 | |||
75 | return mipi_dsi[handle]; | ||
76 | } | ||
77 | |||
78 | static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, | ||
79 | u8 cmd, u8 param) | ||
80 | { | ||
81 | u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8); | ||
82 | int cnt = 100; | ||
83 | |||
84 | /* transmit a short packet to LCD panel */ | ||
85 | iowrite32(1 | data, mipi->linkbase + CMTSRTCTR); | ||
86 | iowrite32(1, mipi->linkbase + CMTSRTREQ); | ||
87 | |||
88 | while ((ioread32(mipi->linkbase + CMTSRTREQ) & 1) && --cnt) | ||
89 | udelay(1); | ||
90 | |||
91 | return cnt ? 0 : -ETIMEDOUT; | ||
92 | } | ||
93 | |||
94 | #define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \ | ||
95 | -EINVAL : (c) - 1) | ||
96 | |||
97 | static int sh_mipi_dcs(int handle, u8 cmd) | ||
98 | { | ||
99 | struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); | ||
100 | if (!mipi) | ||
101 | return -ENODEV; | ||
102 | return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0); | ||
103 | } | ||
104 | |||
105 | static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param) | ||
106 | { | ||
107 | struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle)); | ||
108 | if (!mipi) | ||
109 | return -ENODEV; | ||
110 | return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd, | ||
111 | param); | ||
112 | } | ||
113 | |||
114 | static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) | ||
115 | { | ||
116 | /* | ||
117 | * enable LCDC data tx, transition to LPS after completion of each HS | ||
118 | * packet | ||
119 | */ | ||
120 | iowrite32(0x00000002 | enable, mipi->linkbase + DTCTR); | ||
121 | } | ||
122 | |||
123 | static void sh_mipi_shutdown(struct platform_device *pdev) | ||
124 | { | ||
125 | struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); | ||
126 | |||
127 | sh_mipi_dsi_enable(mipi, false); | ||
128 | } | ||
129 | |||
130 | static int sh_mipi_setup(struct sh_mipi *mipi, const struct fb_videomode *mode) | ||
131 | { | ||
132 | void __iomem *base = mipi->base; | ||
133 | struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; | ||
134 | u32 pctype, datatype, pixfmt, linelength, vmctr2; | ||
135 | u32 tmp, top, bottom, delay, div; | ||
136 | int bpp; | ||
137 | |||
138 | /* | ||
139 | * Select data format. MIPI DSI is not hot-pluggable, so, we just use | ||
140 | * the default videomode. If this ever becomes a problem, We'll have to | ||
141 | * move this to mipi_display_on() above and use info->var.xres | ||
142 | */ | ||
143 | switch (pdata->data_format) { | ||
144 | case MIPI_RGB888: | ||
145 | pctype = 0; | ||
146 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | ||
147 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
148 | linelength = mode->xres * 3; | ||
149 | break; | ||
150 | case MIPI_RGB565: | ||
151 | pctype = 1; | ||
152 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | ||
153 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
154 | linelength = mode->xres * 2; | ||
155 | break; | ||
156 | case MIPI_RGB666_LP: | ||
157 | pctype = 2; | ||
158 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | ||
159 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
160 | linelength = mode->xres * 3; | ||
161 | break; | ||
162 | case MIPI_RGB666: | ||
163 | pctype = 3; | ||
164 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | ||
165 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | ||
166 | linelength = (mode->xres * 18 + 7) / 8; | ||
167 | break; | ||
168 | case MIPI_BGR888: | ||
169 | pctype = 8; | ||
170 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | ||
171 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
172 | linelength = mode->xres * 3; | ||
173 | break; | ||
174 | case MIPI_BGR565: | ||
175 | pctype = 9; | ||
176 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | ||
177 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
178 | linelength = mode->xres * 2; | ||
179 | break; | ||
180 | case MIPI_BGR666_LP: | ||
181 | pctype = 0xa; | ||
182 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | ||
183 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | ||
184 | linelength = mode->xres * 3; | ||
185 | break; | ||
186 | case MIPI_BGR666: | ||
187 | pctype = 0xb; | ||
188 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | ||
189 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | ||
190 | linelength = (mode->xres * 18 + 7) / 8; | ||
191 | break; | ||
192 | case MIPI_YUYV: | ||
193 | pctype = 4; | ||
194 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | ||
195 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
196 | linelength = mode->xres * 2; | ||
197 | break; | ||
198 | case MIPI_UYVY: | ||
199 | pctype = 5; | ||
200 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | ||
201 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | ||
202 | linelength = mode->xres * 2; | ||
203 | break; | ||
204 | case MIPI_YUV420_L: | ||
205 | pctype = 6; | ||
206 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | ||
207 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | ||
208 | linelength = (mode->xres * 12 + 7) / 8; | ||
209 | break; | ||
210 | case MIPI_YUV420: | ||
211 | pctype = 7; | ||
212 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | ||
213 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | ||
214 | /* Length of U/V line */ | ||
215 | linelength = (mode->xres + 1) / 2; | ||
216 | break; | ||
217 | default: | ||
218 | return -EINVAL; | ||
219 | } | ||
220 | |||
221 | if (!pdata->lane) | ||
222 | return -EINVAL; | ||
223 | |||
224 | /* reset DSI link */ | ||
225 | iowrite32(0x00000001, base + SYSCTRL); | ||
226 | /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ | ||
227 | udelay(50); | ||
228 | iowrite32(0x00000000, base + SYSCTRL); | ||
229 | |||
230 | /* setup DSI link */ | ||
231 | |||
232 | /* | ||
233 | * T_wakeup = 0x7000 | ||
234 | * T_hs-trail = 3 | ||
235 | * T_hs-prepare = 3 | ||
236 | * T_clk-trail = 3 | ||
237 | * T_clk-prepare = 2 | ||
238 | */ | ||
239 | iowrite32(0x70003332, base + TIMSET); | ||
240 | /* no responses requested */ | ||
241 | iowrite32(0x00000000, base + RESREQSET0); | ||
242 | /* request response to packets of type 0x28 */ | ||
243 | iowrite32(0x00000100, base + RESREQSET1); | ||
244 | /* High-speed transmission timeout, default 0xffffffff */ | ||
245 | iowrite32(0x0fffffff, base + HSTTOVSET); | ||
246 | /* LP reception timeout, default 0xffffffff */ | ||
247 | iowrite32(0x0fffffff, base + LPRTOVSET); | ||
248 | /* Turn-around timeout, default 0xffffffff */ | ||
249 | iowrite32(0x0fffffff, base + TATOVSET); | ||
250 | /* Peripheral reset timeout, default 0xffffffff */ | ||
251 | iowrite32(0x0fffffff, base + PRTOVSET); | ||
252 | /* Interrupts not used, disable all */ | ||
253 | iowrite32(0, base + DSIINTE); | ||
254 | /* DSI-Tx bias on */ | ||
255 | iowrite32(0x00000001, base + PHYCTRL); | ||
256 | udelay(200); | ||
257 | /* Deassert resets, power on */ | ||
258 | iowrite32(0x03070001 | pdata->phyctrl, base + PHYCTRL); | ||
259 | |||
260 | /* | ||
261 | * Default = ULPS enable | | ||
262 | * Contention detection enabled | | ||
263 | * EoT packet transmission enable | | ||
264 | * CRC check enable | | ||
265 | * ECC check enable | ||
266 | */ | ||
267 | bitmap_fill((unsigned long *)&tmp, pdata->lane); | ||
268 | tmp |= 0x00003700; | ||
269 | iowrite32(tmp, base + SYSCONF); | ||
270 | |||
271 | /* setup l-bridge */ | ||
272 | |||
273 | /* | ||
274 | * Enable transmission of all packets, | ||
275 | * transmit LPS after each HS packet completion | ||
276 | */ | ||
277 | iowrite32(0x00000006, mipi->linkbase + DTCTR); | ||
278 | /* VSYNC width = 2 (<< 17) */ | ||
279 | iowrite32((mode->vsync_len << pdata->vsynw_offset) | | ||
280 | (pdata->clksrc << 16) | (pctype << 12) | datatype, | ||
281 | mipi->linkbase + VMCTR1); | ||
282 | |||
283 | /* | ||
284 | * Non-burst mode with sync pulses: VSE and HSE are output, | ||
285 | * HSA period allowed, no commands in LP | ||
286 | */ | ||
287 | vmctr2 = 0; | ||
288 | if (pdata->flags & SH_MIPI_DSI_VSEE) | ||
289 | vmctr2 |= 1 << 23; | ||
290 | if (pdata->flags & SH_MIPI_DSI_HSEE) | ||
291 | vmctr2 |= 1 << 22; | ||
292 | if (pdata->flags & SH_MIPI_DSI_HSAE) | ||
293 | vmctr2 |= 1 << 21; | ||
294 | if (pdata->flags & SH_MIPI_DSI_BL2E) | ||
295 | vmctr2 |= 1 << 17; | ||
296 | if (pdata->flags & SH_MIPI_DSI_HSABM) | ||
297 | vmctr2 |= 1 << 5; | ||
298 | if (pdata->flags & SH_MIPI_DSI_HBPBM) | ||
299 | vmctr2 |= 1 << 4; | ||
300 | if (pdata->flags & SH_MIPI_DSI_HFPBM) | ||
301 | vmctr2 |= 1 << 3; | ||
302 | iowrite32(vmctr2, mipi->linkbase + VMCTR2); | ||
303 | |||
304 | /* | ||
305 | * VMLEN1 = RGBLEN | HSALEN | ||
306 | * | ||
307 | * see | ||
308 | * Video mode - Blanking Packet setting | ||
309 | */ | ||
310 | top = linelength << 16; /* RGBLEN */ | ||
311 | bottom = 0x00000001; | ||
312 | if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ | ||
313 | bottom = (pdata->lane * mode->hsync_len) - 10; | ||
314 | iowrite32(top | bottom , mipi->linkbase + VMLEN1); | ||
315 | |||
316 | /* | ||
317 | * VMLEN2 = HBPLEN | HFPLEN | ||
318 | * | ||
319 | * see | ||
320 | * Video mode - Blanking Packet setting | ||
321 | */ | ||
322 | top = 0x00010000; | ||
323 | bottom = 0x00000001; | ||
324 | delay = 0; | ||
325 | |||
326 | div = 1; /* HSbyteCLK is calculation base | ||
327 | * HS4divCLK = HSbyteCLK/2 | ||
328 | * HS6divCLK is not supported for now */ | ||
329 | if (pdata->flags & SH_MIPI_DSI_HS4divCLK) | ||
330 | div = 2; | ||
331 | |||
332 | if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ | ||
333 | top = mode->hsync_len + mode->left_margin; | ||
334 | top = ((pdata->lane * top / div) - 10) << 16; | ||
335 | } | ||
336 | if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ | ||
337 | bottom = mode->right_margin; | ||
338 | bottom = (pdata->lane * bottom / div) - 12; | ||
339 | } | ||
340 | |||
341 | bpp = linelength / mode->xres; /* byte / pixel */ | ||
342 | if ((pdata->lane / div) > bpp) { | ||
343 | tmp = mode->xres / bpp; /* output cycle */ | ||
344 | tmp = mode->xres - tmp; /* (input - output) cycle */ | ||
345 | delay = (pdata->lane * tmp); | ||
346 | } | ||
347 | |||
348 | iowrite32(top | (bottom + delay) , mipi->linkbase + VMLEN2); | ||
349 | |||
350 | msleep(5); | ||
351 | |||
352 | /* setup LCD panel */ | ||
353 | |||
354 | /* cf. drivers/video/omap/lcd_mipid.c */ | ||
355 | sh_mipi_dcs(pdata->channel, MIPI_DCS_EXIT_SLEEP_MODE); | ||
356 | msleep(120); | ||
357 | /* | ||
358 | * [7] - Page Address Mode | ||
359 | * [6] - Column Address Mode | ||
360 | * [5] - Page / Column Address Mode | ||
361 | * [4] - Display Device Line Refresh Order | ||
362 | * [3] - RGB/BGR Order | ||
363 | * [2] - Display Data Latch Data Order | ||
364 | * [1] - Flip Horizontal | ||
365 | * [0] - Flip Vertical | ||
366 | */ | ||
367 | sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_ADDRESS_MODE, 0x00); | ||
368 | /* cf. set_data_lines() */ | ||
369 | sh_mipi_dcs_param(pdata->channel, MIPI_DCS_SET_PIXEL_FORMAT, | ||
370 | pixfmt << 4); | ||
371 | sh_mipi_dcs(pdata->channel, MIPI_DCS_SET_DISPLAY_ON); | ||
372 | |||
373 | /* Enable timeout counters */ | ||
374 | iowrite32(0x00000f00, base + DSICTRL); | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int mipi_display_on(struct sh_mobile_lcdc_entity *entity) | ||
380 | { | ||
381 | struct sh_mipi *mipi = to_sh_mipi(entity); | ||
382 | struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; | ||
383 | int ret; | ||
384 | |||
385 | pm_runtime_get_sync(&mipi->pdev->dev); | ||
386 | |||
387 | ret = pdata->set_dot_clock(mipi->pdev, mipi->base, 1); | ||
388 | if (ret < 0) | ||
389 | goto mipi_display_on_fail1; | ||
390 | |||
391 | ret = sh_mipi_setup(mipi, &entity->def_mode); | ||
392 | if (ret < 0) | ||
393 | goto mipi_display_on_fail2; | ||
394 | |||
395 | sh_mipi_dsi_enable(mipi, true); | ||
396 | |||
397 | return SH_MOBILE_LCDC_DISPLAY_CONNECTED; | ||
398 | |||
399 | mipi_display_on_fail1: | ||
400 | pm_runtime_put_sync(&mipi->pdev->dev); | ||
401 | mipi_display_on_fail2: | ||
402 | pdata->set_dot_clock(mipi->pdev, mipi->base, 0); | ||
403 | |||
404 | return ret; | ||
405 | } | ||
406 | |||
407 | static void mipi_display_off(struct sh_mobile_lcdc_entity *entity) | ||
408 | { | ||
409 | struct sh_mipi *mipi = to_sh_mipi(entity); | ||
410 | struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; | ||
411 | |||
412 | sh_mipi_dsi_enable(mipi, false); | ||
413 | |||
414 | pdata->set_dot_clock(mipi->pdev, mipi->base, 0); | ||
415 | |||
416 | pm_runtime_put_sync(&mipi->pdev->dev); | ||
417 | } | ||
418 | |||
419 | static const struct sh_mobile_lcdc_entity_ops mipi_ops = { | ||
420 | .display_on = mipi_display_on, | ||
421 | .display_off = mipi_display_off, | ||
422 | }; | ||
423 | |||
424 | static int __init sh_mipi_probe(struct platform_device *pdev) | ||
425 | { | ||
426 | struct sh_mipi *mipi; | ||
427 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; | ||
428 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
429 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
430 | unsigned long rate, f_current; | ||
431 | int idx = pdev->id, ret; | ||
432 | |||
433 | if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) | ||
434 | return -ENODEV; | ||
435 | |||
436 | if (!pdata->set_dot_clock) | ||
437 | return -EINVAL; | ||
438 | |||
439 | mutex_lock(&array_lock); | ||
440 | if (idx < 0) | ||
441 | for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++) | ||
442 | ; | ||
443 | |||
444 | if (idx == ARRAY_SIZE(mipi_dsi)) { | ||
445 | ret = -EBUSY; | ||
446 | goto efindslot; | ||
447 | } | ||
448 | |||
449 | mipi = kzalloc(sizeof(*mipi), GFP_KERNEL); | ||
450 | if (!mipi) { | ||
451 | ret = -ENOMEM; | ||
452 | goto ealloc; | ||
453 | } | ||
454 | |||
455 | mipi->entity.owner = THIS_MODULE; | ||
456 | mipi->entity.ops = &mipi_ops; | ||
457 | |||
458 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | ||
459 | dev_err(&pdev->dev, "MIPI register region already claimed\n"); | ||
460 | ret = -EBUSY; | ||
461 | goto ereqreg; | ||
462 | } | ||
463 | |||
464 | mipi->base = ioremap(res->start, resource_size(res)); | ||
465 | if (!mipi->base) { | ||
466 | ret = -ENOMEM; | ||
467 | goto emap; | ||
468 | } | ||
469 | |||
470 | if (!request_mem_region(res2->start, resource_size(res2), pdev->name)) { | ||
471 | dev_err(&pdev->dev, "MIPI register region 2 already claimed\n"); | ||
472 | ret = -EBUSY; | ||
473 | goto ereqreg2; | ||
474 | } | ||
475 | |||
476 | mipi->linkbase = ioremap(res2->start, resource_size(res2)); | ||
477 | if (!mipi->linkbase) { | ||
478 | ret = -ENOMEM; | ||
479 | goto emap2; | ||
480 | } | ||
481 | |||
482 | mipi->pdev = pdev; | ||
483 | |||
484 | mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); | ||
485 | if (IS_ERR(mipi->dsit_clk)) { | ||
486 | ret = PTR_ERR(mipi->dsit_clk); | ||
487 | goto eclktget; | ||
488 | } | ||
489 | |||
490 | f_current = clk_get_rate(mipi->dsit_clk); | ||
491 | /* 80MHz required by the datasheet */ | ||
492 | rate = clk_round_rate(mipi->dsit_clk, 80000000); | ||
493 | if (rate > 0 && rate != f_current) | ||
494 | ret = clk_set_rate(mipi->dsit_clk, rate); | ||
495 | else | ||
496 | ret = rate; | ||
497 | if (ret < 0) | ||
498 | goto esettrate; | ||
499 | |||
500 | dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate); | ||
501 | |||
502 | ret = clk_enable(mipi->dsit_clk); | ||
503 | if (ret < 0) | ||
504 | goto eclkton; | ||
505 | |||
506 | mipi_dsi[idx] = mipi; | ||
507 | |||
508 | pm_runtime_enable(&pdev->dev); | ||
509 | pm_runtime_resume(&pdev->dev); | ||
510 | |||
511 | mutex_unlock(&array_lock); | ||
512 | platform_set_drvdata(pdev, &mipi->entity); | ||
513 | |||
514 | return 0; | ||
515 | |||
516 | eclkton: | ||
517 | esettrate: | ||
518 | clk_put(mipi->dsit_clk); | ||
519 | eclktget: | ||
520 | iounmap(mipi->linkbase); | ||
521 | emap2: | ||
522 | release_mem_region(res2->start, resource_size(res2)); | ||
523 | ereqreg2: | ||
524 | iounmap(mipi->base); | ||
525 | emap: | ||
526 | release_mem_region(res->start, resource_size(res)); | ||
527 | ereqreg: | ||
528 | kfree(mipi); | ||
529 | ealloc: | ||
530 | efindslot: | ||
531 | mutex_unlock(&array_lock); | ||
532 | |||
533 | return ret; | ||
534 | } | ||
535 | |||
536 | static int sh_mipi_remove(struct platform_device *pdev) | ||
537 | { | ||
538 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
539 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
540 | struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev)); | ||
541 | int i, ret; | ||
542 | |||
543 | mutex_lock(&array_lock); | ||
544 | |||
545 | for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++) | ||
546 | ; | ||
547 | |||
548 | if (i == ARRAY_SIZE(mipi_dsi)) { | ||
549 | ret = -EINVAL; | ||
550 | } else { | ||
551 | ret = 0; | ||
552 | mipi_dsi[i] = NULL; | ||
553 | } | ||
554 | |||
555 | mutex_unlock(&array_lock); | ||
556 | |||
557 | if (ret < 0) | ||
558 | return ret; | ||
559 | |||
560 | pm_runtime_disable(&pdev->dev); | ||
561 | clk_disable(mipi->dsit_clk); | ||
562 | clk_put(mipi->dsit_clk); | ||
563 | |||
564 | iounmap(mipi->linkbase); | ||
565 | if (res2) | ||
566 | release_mem_region(res2->start, resource_size(res2)); | ||
567 | iounmap(mipi->base); | ||
568 | if (res) | ||
569 | release_mem_region(res->start, resource_size(res)); | ||
570 | kfree(mipi); | ||
571 | |||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static struct platform_driver sh_mipi_driver = { | ||
576 | .remove = sh_mipi_remove, | ||
577 | .shutdown = sh_mipi_shutdown, | ||
578 | .driver = { | ||
579 | .name = "sh-mipi-dsi", | ||
580 | }, | ||
581 | }; | ||
582 | |||
583 | module_platform_driver_probe(sh_mipi_driver, sh_mipi_probe); | ||
584 | |||
585 | MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); | ||
586 | MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver"); | ||
587 | MODULE_LICENSE("GPL v2"); | ||