• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/video/
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/clk.h>
12#include <linux/delay.h>
13#include <linux/init.h>
14#include <linux/io.h>
15#include <linux/platform_device.h>
16#include <linux/slab.h>
17#include <linux/string.h>
18#include <linux/types.h>
19
20#include <video/mipi_display.h>
21#include <video/sh_mipi_dsi.h>
22#include <video/sh_mobile_lcdc.h>
23
24#define CMTSRTCTR	0x80d0
25#define CMTSRTREQ	0x8070
26
27#define DSIINTE		0x0060
28
29/* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */
30#define MAX_SH_MIPI_DSI 2
31
32struct sh_mipi {
33	void __iomem	*base;
34	struct clk	*dsit_clk;
35	struct clk	*dsip_clk;
36};
37
38static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
39
40/* Protect the above array */
41static DEFINE_MUTEX(array_lock);
42
43static struct sh_mipi *sh_mipi_by_handle(int handle)
44{
45	if (handle >= ARRAY_SIZE(mipi_dsi) || handle < 0)
46		return NULL;
47
48	return mipi_dsi[handle];
49}
50
51static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd,
52			      u8 cmd, u8 param)
53{
54	u32 data = (dsi_cmd << 24) | (cmd << 16) | (param << 8);
55	int cnt = 100;
56
57	/* transmit a short packet to LCD panel */
58	iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */
59	iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */
60
61	while ((ioread32(mipi->base + 0x8070) & 1) && --cnt)
62		udelay(1);
63
64	return cnt ? 0 : -ETIMEDOUT;
65}
66
67#define LCD_CHAN2MIPI(c) ((c) < LCDC_CHAN_MAINLCD || (c) > LCDC_CHAN_SUBLCD ? \
68				-EINVAL : (c) - 1)
69
70static int sh_mipi_dcs(int handle, u8 cmd)
71{
72	struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
73	if (!mipi)
74		return -ENODEV;
75	return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
76}
77
78static int sh_mipi_dcs_param(int handle, u8 cmd, u8 param)
79{
80	struct sh_mipi *mipi = sh_mipi_by_handle(LCD_CHAN2MIPI(handle));
81	if (!mipi)
82		return -ENODEV;
83	return sh_mipi_send_short(mipi, MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd,
84				  param);
85}
86
87static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
88{
89	/*
90	 * enable LCDC data tx, transition to LPS after completion of each HS
91	 * packet
92	 */
93	iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */
94}
95
96static void sh_mipi_shutdown(struct platform_device *pdev)
97{
98	struct sh_mipi *mipi = platform_get_drvdata(pdev);
99
100	sh_mipi_dsi_enable(mipi, false);
101}
102
103static void mipi_display_on(void *arg, struct fb_info *info)
104{
105	struct sh_mipi *mipi = arg;
106
107	sh_mipi_dsi_enable(mipi, true);
108}
109
110static void mipi_display_off(void *arg)
111{
112	struct sh_mipi *mipi = arg;
113
114	sh_mipi_dsi_enable(mipi, false);
115}
116
117static int __init sh_mipi_setup(struct sh_mipi *mipi,
118				struct sh_mipi_dsi_info *pdata)
119{
120	void __iomem *base = mipi->base;
121	struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
122	u32 pctype, datatype, pixfmt;
123	u32 linelength;
124	bool yuv;
125
126	/* Select data format */
127	switch (pdata->data_format) {
128	case MIPI_RGB888:
129		pctype = 0;
130		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
131		pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
132		linelength = ch->lcd_cfg.xres * 3;
133		yuv = false;
134		break;
135	case MIPI_RGB565:
136		pctype = 1;
137		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
138		pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
139		linelength = ch->lcd_cfg.xres * 2;
140		yuv = false;
141		break;
142	case MIPI_RGB666_LP:
143		pctype = 2;
144		datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
145		pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
146		linelength = ch->lcd_cfg.xres * 3;
147		yuv = false;
148		break;
149	case MIPI_RGB666:
150		pctype = 3;
151		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
152		pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
153		linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
154		yuv = false;
155		break;
156	case MIPI_BGR888:
157		pctype = 8;
158		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
159		pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
160		linelength = ch->lcd_cfg.xres * 3;
161		yuv = false;
162		break;
163	case MIPI_BGR565:
164		pctype = 9;
165		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
166		pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
167		linelength = ch->lcd_cfg.xres * 2;
168		yuv = false;
169		break;
170	case MIPI_BGR666_LP:
171		pctype = 0xa;
172		datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
173		pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
174		linelength = ch->lcd_cfg.xres * 3;
175		yuv = false;
176		break;
177	case MIPI_BGR666:
178		pctype = 0xb;
179		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
180		pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
181		linelength = (ch->lcd_cfg.xres * 18 + 7) / 8;
182		yuv = false;
183		break;
184	case MIPI_YUYV:
185		pctype = 4;
186		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
187		pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
188		linelength = ch->lcd_cfg.xres * 2;
189		yuv = true;
190		break;
191	case MIPI_UYVY:
192		pctype = 5;
193		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
194		pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
195		linelength = ch->lcd_cfg.xres * 2;
196		yuv = true;
197		break;
198	case MIPI_YUV420_L:
199		pctype = 6;
200		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
201		pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
202		linelength = (ch->lcd_cfg.xres * 12 + 7) / 8;
203		yuv = true;
204		break;
205	case MIPI_YUV420:
206		pctype = 7;
207		datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
208		pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
209		/* Length of U/V line */
210		linelength = (ch->lcd_cfg.xres + 1) / 2;
211		yuv = true;
212		break;
213	default:
214		return -EINVAL;
215	}
216
217	if ((yuv && ch->interface_type != YUV422) ||
218	    (!yuv && ch->interface_type != RGB24))
219		return -EINVAL;
220
221	/* reset DSI link */
222	iowrite32(0x00000001, base); /* SYSCTRL */
223	/* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
224	udelay(50);
225	iowrite32(0x00000000, base); /* SYSCTRL */
226
227	/* setup DSI link */
228
229	/*
230	 * Default = ULPS enable |
231	 *	Contention detection enabled |
232	 *	EoT packet transmission enable |
233	 *	CRC check enable |
234	 *	ECC check enable
235	 * additionally enable first two lanes
236	 */
237	iowrite32(0x00003703, base + 0x04); /* SYSCONF */
238	/*
239	 * T_wakeup = 0x7000
240	 * T_hs-trail = 3
241	 * T_hs-prepare = 3
242	 * T_clk-trail = 3
243	 * T_clk-prepare = 2
244	 */
245	iowrite32(0x70003332, base + 0x08); /* TIMSET */
246	/* no responses requested */
247	iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */
248	/* request response to packets of type 0x28 */
249	iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */
250	/* High-speed transmission timeout, default 0xffffffff */
251	iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */
252	/* LP reception timeout, default 0xffffffff */
253	iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */
254	/* Turn-around timeout, default 0xffffffff */
255	iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */
256	/* Peripheral reset timeout, default 0xffffffff */
257	iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */
258	/* Enable timeout counters */
259	iowrite32(0x00000f00, base + 0x30); /* DSICTRL */
260	/* Interrupts not used, disable all */
261	iowrite32(0, base + DSIINTE);
262	/* DSI-Tx bias on */
263	iowrite32(0x00000001, base + 0x70); /* PHYCTRL */
264	udelay(200);
265	/* Deassert resets, power on, set multiplier */
266	iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */
267
268	/* setup l-bridge */
269
270	/*
271	 * Enable transmission of all packets,
272	 * transmit LPS after each HS packet completion
273	 */
274	iowrite32(0x00000006, base + 0x8000); /* DTCTR */
275	/* VSYNC width = 2 (<< 17) */
276	iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */
277	/*
278	 * Non-burst mode with sync pulses: VSE and HSE are output,
279	 * HSA period allowed, no commands in LP
280	 */
281	iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */
282	/*
283	 * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
284	 * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default
285	 * (unused, since VMCTR2[HSABM] = 0)
286	 */
287	iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */
288
289	msleep(5);
290
291	/* setup LCD panel */
292
293	/* cf. drivers/video/omap/lcd_mipid.c */
294	sh_mipi_dcs(ch->chan, MIPI_DCS_EXIT_SLEEP_MODE);
295	msleep(120);
296	/*
297	 * [7] - Page Address Mode
298	 * [6] - Column Address Mode
299	 * [5] - Page / Column Address Mode
300	 * [4] - Display Device Line Refresh Order
301	 * [3] - RGB/BGR Order
302	 * [2] - Display Data Latch Data Order
303	 * [1] - Flip Horizontal
304	 * [0] - Flip Vertical
305	 */
306	sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_ADDRESS_MODE, 0x00);
307	/* cf. set_data_lines() */
308	sh_mipi_dcs_param(ch->chan, MIPI_DCS_SET_PIXEL_FORMAT,
309			  pixfmt << 4);
310	sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
311
312	return 0;
313}
314
315static int __init sh_mipi_probe(struct platform_device *pdev)
316{
317	struct sh_mipi *mipi;
318	struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
319	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320	unsigned long rate, f_current;
321	int idx = pdev->id, ret;
322	char dsip_clk[] = "dsi.p_clk";
323
324	if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
325		return -ENODEV;
326
327	mutex_lock(&array_lock);
328	if (idx < 0)
329		for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
330			;
331
332	if (idx == ARRAY_SIZE(mipi_dsi)) {
333		ret = -EBUSY;
334		goto efindslot;
335	}
336
337	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
338	if (!mipi) {
339		ret = -ENOMEM;
340		goto ealloc;
341	}
342
343	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
344		dev_err(&pdev->dev, "MIPI register region already claimed\n");
345		ret = -EBUSY;
346		goto ereqreg;
347	}
348
349	mipi->base = ioremap(res->start, resource_size(res));
350	if (!mipi->base) {
351		ret = -ENOMEM;
352		goto emap;
353	}
354
355	mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
356	if (IS_ERR(mipi->dsit_clk)) {
357		ret = PTR_ERR(mipi->dsit_clk);
358		goto eclktget;
359	}
360
361	f_current = clk_get_rate(mipi->dsit_clk);
362	/* 80MHz required by the datasheet */
363	rate = clk_round_rate(mipi->dsit_clk, 80000000);
364	if (rate > 0 && rate != f_current)
365		ret = clk_set_rate(mipi->dsit_clk, rate);
366	else
367		ret = rate;
368	if (ret < 0)
369		goto esettrate;
370
371	dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
372
373	sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
374	mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
375	if (IS_ERR(mipi->dsip_clk)) {
376		ret = PTR_ERR(mipi->dsip_clk);
377		goto eclkpget;
378	}
379
380	f_current = clk_get_rate(mipi->dsip_clk);
381	/* Between 10 and 50MHz */
382	rate = clk_round_rate(mipi->dsip_clk, 24000000);
383	if (rate > 0 && rate != f_current)
384		ret = clk_set_rate(mipi->dsip_clk, rate);
385	else
386		ret = rate;
387	if (ret < 0)
388		goto esetprate;
389
390	dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
391
392	msleep(10);
393
394	ret = clk_enable(mipi->dsit_clk);
395	if (ret < 0)
396		goto eclkton;
397
398	ret = clk_enable(mipi->dsip_clk);
399	if (ret < 0)
400		goto eclkpon;
401
402	mipi_dsi[idx] = mipi;
403
404	ret = sh_mipi_setup(mipi, pdata);
405	if (ret < 0)
406		goto emipisetup;
407
408	mutex_unlock(&array_lock);
409	platform_set_drvdata(pdev, mipi);
410
411	/* Set up LCDC callbacks */
412	pdata->lcd_chan->board_cfg.board_data = mipi;
413	pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
414	pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
415
416	return 0;
417
418emipisetup:
419	mipi_dsi[idx] = NULL;
420	clk_disable(mipi->dsip_clk);
421eclkpon:
422	clk_disable(mipi->dsit_clk);
423eclkton:
424esetprate:
425	clk_put(mipi->dsip_clk);
426eclkpget:
427esettrate:
428	clk_put(mipi->dsit_clk);
429eclktget:
430	iounmap(mipi->base);
431emap:
432	release_mem_region(res->start, resource_size(res));
433ereqreg:
434	kfree(mipi);
435ealloc:
436efindslot:
437	mutex_unlock(&array_lock);
438
439	return ret;
440}
441
442static int __exit sh_mipi_remove(struct platform_device *pdev)
443{
444	struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
445	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
446	struct sh_mipi *mipi = platform_get_drvdata(pdev);
447	int i, ret;
448
449	mutex_lock(&array_lock);
450
451	for (i = 0; i < ARRAY_SIZE(mipi_dsi) && mipi_dsi[i] != mipi; i++)
452		;
453
454	if (i == ARRAY_SIZE(mipi_dsi)) {
455		ret = -EINVAL;
456	} else {
457		ret = 0;
458		mipi_dsi[i] = NULL;
459	}
460
461	mutex_unlock(&array_lock);
462
463	if (ret < 0)
464		return ret;
465
466	pdata->lcd_chan->board_cfg.display_on = NULL;
467	pdata->lcd_chan->board_cfg.display_off = NULL;
468	pdata->lcd_chan->board_cfg.board_data = NULL;
469
470	clk_disable(mipi->dsip_clk);
471	clk_disable(mipi->dsit_clk);
472	clk_put(mipi->dsit_clk);
473	clk_put(mipi->dsip_clk);
474	iounmap(mipi->base);
475	if (res)
476		release_mem_region(res->start, resource_size(res));
477	platform_set_drvdata(pdev, NULL);
478	kfree(mipi);
479
480	return 0;
481}
482
483static struct platform_driver sh_mipi_driver = {
484	.remove		= __exit_p(sh_mipi_remove),
485	.shutdown	= sh_mipi_shutdown,
486	.driver = {
487		.name	= "sh-mipi-dsi",
488	},
489};
490
491static int __init sh_mipi_init(void)
492{
493	return platform_driver_probe(&sh_mipi_driver, sh_mipi_probe);
494}
495module_init(sh_mipi_init);
496
497static void __exit sh_mipi_exit(void)
498{
499	platform_driver_unregister(&sh_mipi_driver);
500}
501module_exit(sh_mipi_exit);
502
503MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
504MODULE_DESCRIPTION("SuperH / ARM-shmobile MIPI DSI driver");
505MODULE_LICENSE("GPL v2");
506