• 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.36/drivers/video/omap/
1/*
2 * OMAP1 Special OptimiSed Screen Interface support
3 *
4 * Copyright (C) 2004-2005 Nokia Corporation
5 * Author: Juha Yrj�l� <juha.yrjola@nokia.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21#include <linux/module.h>
22#include <linux/mm.h>
23#include <linux/clk.h>
24#include <linux/irq.h>
25#include <linux/io.h>
26#include <linux/interrupt.h>
27
28#include <plat/dma.h>
29
30#include "omapfb.h"
31#include "lcdc.h"
32
33#define MODULE_NAME		"omapfb-sossi"
34
35#define OMAP_SOSSI_BASE         0xfffbac00
36#define SOSSI_ID_REG		0x00
37#define SOSSI_INIT1_REG		0x04
38#define SOSSI_INIT2_REG		0x08
39#define SOSSI_INIT3_REG		0x0c
40#define SOSSI_FIFO_REG		0x10
41#define SOSSI_REOTABLE_REG	0x14
42#define SOSSI_TEARING_REG	0x18
43#define SOSSI_INIT1B_REG	0x1c
44#define SOSSI_FIFOB_REG		0x20
45
46#define DMA_GSCR          0xfffedc04
47#define DMA_LCD_CCR       0xfffee3c2
48#define DMA_LCD_CTRL      0xfffee3c4
49#define DMA_LCD_LCH_CTRL  0xfffee3ea
50
51#define CONF_SOSSI_RESET_R      (1 << 23)
52
53#define RD_ACCESS		0
54#define WR_ACCESS		1
55
56#define SOSSI_MAX_XMIT_BYTES	(512 * 1024)
57
58static struct {
59	void __iomem	*base;
60	struct clk	*fck;
61	unsigned long	fck_hz;
62	spinlock_t	lock;
63	int		bus_pick_count;
64	int		bus_pick_width;
65	int		tearsync_mode;
66	int		tearsync_line;
67	void		(*lcdc_callback)(void *data);
68	void		*lcdc_callback_data;
69	int		vsync_dma_pending;
70	/* timing for read and write access */
71	int		clk_div;
72	u8		clk_tw0[2];
73	u8		clk_tw1[2];
74	/*
75	 * if last_access is the same as current we don't have to change
76	 * the timings
77	 */
78	int		last_access;
79
80	struct omapfb_device	*fbdev;
81} sossi;
82
83static inline u32 sossi_read_reg(int reg)
84{
85	return readl(sossi.base + reg);
86}
87
88static inline u16 sossi_read_reg16(int reg)
89{
90	return readw(sossi.base + reg);
91}
92
93static inline u8 sossi_read_reg8(int reg)
94{
95	return readb(sossi.base + reg);
96}
97
98static inline void sossi_write_reg(int reg, u32 value)
99{
100	writel(value, sossi.base + reg);
101}
102
103static inline void sossi_write_reg16(int reg, u16 value)
104{
105	writew(value, sossi.base + reg);
106}
107
108static inline void sossi_write_reg8(int reg, u8 value)
109{
110	writeb(value, sossi.base + reg);
111}
112
113static void sossi_set_bits(int reg, u32 bits)
114{
115	sossi_write_reg(reg, sossi_read_reg(reg) | bits);
116}
117
118static void sossi_clear_bits(int reg, u32 bits)
119{
120	sossi_write_reg(reg, sossi_read_reg(reg) & ~bits);
121}
122
123#define HZ_TO_PS(x)	(1000000000 / (x / 1000))
124
125static u32 ps_to_sossi_ticks(u32 ps, int div)
126{
127	u32 clk_period = HZ_TO_PS(sossi.fck_hz) * div;
128	return (clk_period + ps - 1) / clk_period;
129}
130
131static int calc_rd_timings(struct extif_timings *t)
132{
133	u32 tw0, tw1;
134	int reon, reoff, recyc, actim;
135	int div = t->clk_div;
136
137	/*
138	 * Make sure that after conversion it still holds that:
139	 * reoff > reon, recyc >= reoff, actim > reon
140	 */
141	reon = ps_to_sossi_ticks(t->re_on_time, div);
142	/* reon will be exactly one sossi tick */
143	if (reon > 1)
144		return -1;
145
146	reoff = ps_to_sossi_ticks(t->re_off_time, div);
147
148	if (reoff <= reon)
149		reoff = reon + 1;
150
151	tw0 = reoff - reon;
152	if (tw0 > 0x10)
153		return -1;
154
155	recyc = ps_to_sossi_ticks(t->re_cycle_time, div);
156	if (recyc <= reoff)
157		recyc = reoff + 1;
158
159	tw1 = recyc - tw0;
160	/* values less then 3 result in the SOSSI block resetting itself */
161	if (tw1 < 3)
162		tw1 = 3;
163	if (tw1 > 0x40)
164		return -1;
165
166	actim = ps_to_sossi_ticks(t->access_time, div);
167	if (actim < reoff)
168		actim++;
169	/*
170	 * access time (data hold time) will be exactly one sossi
171	 * tick
172	 */
173	if (actim - reoff > 1)
174		return -1;
175
176	t->tim[0] = tw0 - 1;
177	t->tim[1] = tw1 - 1;
178
179	return 0;
180}
181
182static int calc_wr_timings(struct extif_timings *t)
183{
184	u32 tw0, tw1;
185	int weon, weoff, wecyc;
186	int div = t->clk_div;
187
188	/*
189	 * Make sure that after conversion it still holds that:
190	 * weoff > weon, wecyc >= weoff
191	 */
192	weon = ps_to_sossi_ticks(t->we_on_time, div);
193	/* weon will be exactly one sossi tick */
194	if (weon > 1)
195		return -1;
196
197	weoff = ps_to_sossi_ticks(t->we_off_time, div);
198	if (weoff <= weon)
199		weoff = weon + 1;
200	tw0 = weoff - weon;
201	if (tw0 > 0x10)
202		return -1;
203
204	wecyc = ps_to_sossi_ticks(t->we_cycle_time, div);
205	if (wecyc <= weoff)
206		wecyc = weoff + 1;
207
208	tw1 = wecyc - tw0;
209	/* values less then 3 result in the SOSSI block resetting itself */
210	if (tw1 < 3)
211		tw1 = 3;
212	if (tw1 > 0x40)
213		return -1;
214
215	t->tim[2] = tw0 - 1;
216	t->tim[3] = tw1 - 1;
217
218	return 0;
219}
220
221static void _set_timing(int div, int tw0, int tw1)
222{
223	u32 l;
224
225#ifdef VERBOSE
226	dev_dbg(sossi.fbdev->dev, "Using TW0 = %d, TW1 = %d, div = %d\n",
227		 tw0 + 1, tw1 + 1, div);
228#endif
229
230	clk_set_rate(sossi.fck, sossi.fck_hz / div);
231	clk_enable(sossi.fck);
232	l = sossi_read_reg(SOSSI_INIT1_REG);
233	l &= ~((0x0f << 20) | (0x3f << 24));
234	l |= (tw0 << 20) | (tw1 << 24);
235	sossi_write_reg(SOSSI_INIT1_REG, l);
236	clk_disable(sossi.fck);
237}
238
239static void _set_bits_per_cycle(int bus_pick_count, int bus_pick_width)
240{
241	u32 l;
242
243	l = sossi_read_reg(SOSSI_INIT3_REG);
244	l &= ~0x3ff;
245	l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f);
246	sossi_write_reg(SOSSI_INIT3_REG, l);
247}
248
249static void _set_tearsync_mode(int mode, unsigned line)
250{
251	u32 l;
252
253	l = sossi_read_reg(SOSSI_TEARING_REG);
254	l &= ~(((1 << 11) - 1) << 15);
255	l |= line << 15;
256	l &= ~(0x3 << 26);
257	l |= mode << 26;
258	sossi_write_reg(SOSSI_TEARING_REG, l);
259	if (mode)
260		sossi_set_bits(SOSSI_INIT2_REG, 1 << 6);	/* TE logic */
261	else
262		sossi_clear_bits(SOSSI_INIT2_REG, 1 << 6);
263}
264
265static inline void set_timing(int access)
266{
267	if (access != sossi.last_access) {
268		sossi.last_access = access;
269		_set_timing(sossi.clk_div,
270			    sossi.clk_tw0[access], sossi.clk_tw1[access]);
271	}
272}
273
274static void sossi_start_transfer(void)
275{
276	/* WE */
277	sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4);
278	/* CS active low */
279	sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30);
280}
281
282static void sossi_stop_transfer(void)
283{
284	/* WE */
285	sossi_set_bits(SOSSI_INIT2_REG, 1 << 4);
286	/* CS active low */
287	sossi_set_bits(SOSSI_INIT1_REG, 1 << 30);
288}
289
290static void wait_end_of_write(void)
291{
292	/* Before reading we must check if some writings are going on */
293	while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3)));
294}
295
296static void send_data(const void *data, unsigned int len)
297{
298	while (len >= 4) {
299		sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data);
300		len -= 4;
301		data += 4;
302	}
303	while (len >= 2) {
304		sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data);
305		len -= 2;
306		data += 2;
307	}
308	while (len) {
309		sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data);
310		len--;
311		data++;
312	}
313}
314
315static void set_cycles(unsigned int len)
316{
317	unsigned long nr_cycles = len / (sossi.bus_pick_width / 8);
318
319	BUG_ON((nr_cycles - 1) & ~0x3ffff);
320
321	sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff);
322	sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff);
323}
324
325static int sossi_convert_timings(struct extif_timings *t)
326{
327	int r = 0;
328	int div = t->clk_div;
329
330	t->converted = 0;
331
332	if (div <= 0 || div > 8)
333		return -1;
334
335	/* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */
336	if ((r = calc_rd_timings(t)) < 0)
337		return r;
338
339	if ((r = calc_wr_timings(t)) < 0)
340		return r;
341
342	t->tim[4] = div;
343
344	t->converted = 1;
345
346	return 0;
347}
348
349static void sossi_set_timings(const struct extif_timings *t)
350{
351	BUG_ON(!t->converted);
352
353	sossi.clk_tw0[RD_ACCESS] = t->tim[0];
354	sossi.clk_tw1[RD_ACCESS] = t->tim[1];
355
356	sossi.clk_tw0[WR_ACCESS] = t->tim[2];
357	sossi.clk_tw1[WR_ACCESS] = t->tim[3];
358
359	sossi.clk_div = t->tim[4];
360}
361
362static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
363{
364	*clk_period = HZ_TO_PS(sossi.fck_hz);
365	*max_clk_div = 8;
366}
367
368static void sossi_set_bits_per_cycle(int bpc)
369{
370	int bus_pick_count, bus_pick_width;
371
372	/*
373	 * We set explicitly the the bus_pick_count as well, although
374	 * with remapping/reordering disabled it will be calculated by HW
375	 * as (32 / bus_pick_width).
376	 */
377	switch (bpc) {
378	case 8:
379		bus_pick_count = 4;
380		bus_pick_width = 8;
381		break;
382	case 16:
383		bus_pick_count = 2;
384		bus_pick_width = 16;
385		break;
386	default:
387		BUG();
388		return;
389	}
390	sossi.bus_pick_width = bus_pick_width;
391	sossi.bus_pick_count = bus_pick_count;
392}
393
394static int sossi_setup_tearsync(unsigned pin_cnt,
395				unsigned hs_pulse_time, unsigned vs_pulse_time,
396				int hs_pol_inv, int vs_pol_inv, int div)
397{
398	int hs, vs;
399	u32 l;
400
401	if (pin_cnt != 1 || div < 1 || div > 8)
402		return -EINVAL;
403
404	hs = ps_to_sossi_ticks(hs_pulse_time, div);
405	vs = ps_to_sossi_ticks(vs_pulse_time, div);
406	if (vs < 8 || vs <= hs || vs >= (1 << 12))
407		return -EDOM;
408	vs /= 8;
409	vs--;
410	if (hs > 8)
411		hs = 8;
412	if (hs)
413		hs--;
414
415	dev_dbg(sossi.fbdev->dev,
416		"setup_tearsync: hs %d vs %d hs_inv %d vs_inv %d\n",
417		hs, vs, hs_pol_inv, vs_pol_inv);
418
419	clk_enable(sossi.fck);
420	l = sossi_read_reg(SOSSI_TEARING_REG);
421	l &= ~((1 << 15) - 1);
422	l |= vs << 3;
423	l |= hs;
424	if (hs_pol_inv)
425		l |= 1 << 29;
426	else
427		l &= ~(1 << 29);
428	if (vs_pol_inv)
429		l |= 1 << 28;
430	else
431		l &= ~(1 << 28);
432	sossi_write_reg(SOSSI_TEARING_REG, l);
433	clk_disable(sossi.fck);
434
435	return 0;
436}
437
438static int sossi_enable_tearsync(int enable, unsigned line)
439{
440	int mode;
441
442	dev_dbg(sossi.fbdev->dev, "tearsync %d line %d\n", enable, line);
443	if (line >= 1 << 11)
444		return -EINVAL;
445	if (enable) {
446		if (line)
447			mode = 2;		/* HS or VS */
448		else
449			mode = 3;		/* VS only */
450	} else
451		mode = 0;
452	sossi.tearsync_line = line;
453	sossi.tearsync_mode = mode;
454
455	return 0;
456}
457
458static void sossi_write_command(const void *data, unsigned int len)
459{
460	clk_enable(sossi.fck);
461	set_timing(WR_ACCESS);
462	_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
463	/* CMD#/DATA */
464	sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18);
465	set_cycles(len);
466	sossi_start_transfer();
467	send_data(data, len);
468	sossi_stop_transfer();
469	wait_end_of_write();
470	clk_disable(sossi.fck);
471}
472
473static void sossi_write_data(const void *data, unsigned int len)
474{
475	clk_enable(sossi.fck);
476	set_timing(WR_ACCESS);
477	_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
478	/* CMD#/DATA */
479	sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
480	set_cycles(len);
481	sossi_start_transfer();
482	send_data(data, len);
483	sossi_stop_transfer();
484	wait_end_of_write();
485	clk_disable(sossi.fck);
486}
487
488static void sossi_transfer_area(int width, int height,
489				void (callback)(void *data), void *data)
490{
491	BUG_ON(callback == NULL);
492
493	sossi.lcdc_callback = callback;
494	sossi.lcdc_callback_data = data;
495
496	clk_enable(sossi.fck);
497	set_timing(WR_ACCESS);
498	_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
499	_set_tearsync_mode(sossi.tearsync_mode, sossi.tearsync_line);
500	/* CMD#/DATA */
501	sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
502	set_cycles(width * height * sossi.bus_pick_width / 8);
503
504	sossi_start_transfer();
505	if (sossi.tearsync_mode) {
506		/*
507		 * Wait for the sync signal and start the transfer only
508		 * then. We can't seem to be able to use HW sync DMA for
509		 * this since LCD DMA shows huge latencies, as if it
510		 * would ignore some of the DMA requests from SoSSI.
511		 */
512		unsigned long flags;
513
514		spin_lock_irqsave(&sossi.lock, flags);
515		sossi.vsync_dma_pending++;
516		spin_unlock_irqrestore(&sossi.lock, flags);
517	} else
518		/* Just start the transfer right away. */
519		omap_enable_lcd_dma();
520}
521
522static void sossi_dma_callback(void *data)
523{
524	omap_stop_lcd_dma();
525	sossi_stop_transfer();
526	clk_disable(sossi.fck);
527	sossi.lcdc_callback(sossi.lcdc_callback_data);
528}
529
530static void sossi_read_data(void *data, unsigned int len)
531{
532	clk_enable(sossi.fck);
533	set_timing(RD_ACCESS);
534	_set_bits_per_cycle(sossi.bus_pick_count, sossi.bus_pick_width);
535	/* CMD#/DATA */
536	sossi_set_bits(SOSSI_INIT1_REG, 1 << 18);
537	set_cycles(len);
538	sossi_start_transfer();
539	while (len >= 4) {
540		*(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG);
541		len -= 4;
542		data += 4;
543	}
544	while (len >= 2) {
545		*(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG);
546		len -= 2;
547		data += 2;
548	}
549	while (len) {
550		*(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG);
551		len--;
552		data++;
553	}
554	sossi_stop_transfer();
555	clk_disable(sossi.fck);
556}
557
558static irqreturn_t sossi_match_irq(int irq, void *data)
559{
560	unsigned long flags;
561
562	spin_lock_irqsave(&sossi.lock, flags);
563	if (sossi.vsync_dma_pending) {
564		sossi.vsync_dma_pending--;
565		omap_enable_lcd_dma();
566	}
567	spin_unlock_irqrestore(&sossi.lock, flags);
568	return IRQ_HANDLED;
569}
570
571static int sossi_init(struct omapfb_device *fbdev)
572{
573	u32 l, k;
574	struct clk *fck;
575	struct clk *dpll1out_ck;
576	int r;
577
578	sossi.base = ioremap(OMAP_SOSSI_BASE, SZ_1K);
579	if (!sossi.base) {
580		dev_err(fbdev->dev, "can't ioremap SoSSI\n");
581		return -ENOMEM;
582	}
583
584	sossi.fbdev = fbdev;
585	spin_lock_init(&sossi.lock);
586
587	dpll1out_ck = clk_get(fbdev->dev, "ck_dpll1out");
588	if (IS_ERR(dpll1out_ck)) {
589		dev_err(fbdev->dev, "can't get DPLL1OUT clock\n");
590		return PTR_ERR(dpll1out_ck);
591	}
592	/*
593	 * We need the parent clock rate, which we might divide further
594	 * depending on the timing requirements of the controller. See
595	 * _set_timings.
596	 */
597	sossi.fck_hz = clk_get_rate(dpll1out_ck);
598	clk_put(dpll1out_ck);
599
600	fck = clk_get(fbdev->dev, "ck_sossi");
601	if (IS_ERR(fck)) {
602		dev_err(fbdev->dev, "can't get SoSSI functional clock\n");
603		return PTR_ERR(fck);
604	}
605	sossi.fck = fck;
606
607	/* Reset and enable the SoSSI module */
608	l = omap_readl(MOD_CONF_CTRL_1);
609	l |= CONF_SOSSI_RESET_R;
610	omap_writel(l, MOD_CONF_CTRL_1);
611	l &= ~CONF_SOSSI_RESET_R;
612	omap_writel(l, MOD_CONF_CTRL_1);
613
614	clk_enable(sossi.fck);
615	l = omap_readl(ARM_IDLECT2);
616	l &= ~(1 << 8);			/* DMACK_REQ */
617	omap_writel(l, ARM_IDLECT2);
618
619	l = sossi_read_reg(SOSSI_INIT2_REG);
620	/* Enable and reset the SoSSI block */
621	l |= (1 << 0) | (1 << 1);
622	sossi_write_reg(SOSSI_INIT2_REG, l);
623	/* Take SoSSI out of reset */
624	l &= ~(1 << 1);
625	sossi_write_reg(SOSSI_INIT2_REG, l);
626
627	sossi_write_reg(SOSSI_ID_REG, 0);
628	l = sossi_read_reg(SOSSI_ID_REG);
629	k = sossi_read_reg(SOSSI_ID_REG);
630
631	if (l != 0x55555555 || k != 0xaaaaaaaa) {
632		dev_err(fbdev->dev,
633			"invalid SoSSI sync pattern: %08x, %08x\n", l, k);
634		r = -ENODEV;
635		goto err;
636	}
637
638	if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) {
639		dev_err(fbdev->dev, "can't get LCDC IRQ\n");
640		r = -ENODEV;
641		goto err;
642	}
643
644	l = sossi_read_reg(SOSSI_ID_REG); /* Component code */
645	l = sossi_read_reg(SOSSI_ID_REG);
646	dev_info(fbdev->dev, "SoSSI version %d.%d initialized\n",
647		l >> 16, l & 0xffff);
648
649	l = sossi_read_reg(SOSSI_INIT1_REG);
650	l |= (1 << 19); /* DMA_MODE */
651	l &= ~(1 << 31); /* REORDERING */
652	sossi_write_reg(SOSSI_INIT1_REG, l);
653
654	if ((r = request_irq(INT_1610_SoSSI_MATCH, sossi_match_irq,
655			     IRQ_TYPE_EDGE_FALLING,
656	     "sossi_match", sossi.fbdev->dev)) < 0) {
657		dev_err(sossi.fbdev->dev, "can't get SoSSI match IRQ\n");
658		goto err;
659	}
660
661	clk_disable(sossi.fck);
662	return 0;
663
664err:
665	clk_disable(sossi.fck);
666	clk_put(sossi.fck);
667	return r;
668}
669
670static void sossi_cleanup(void)
671{
672	omap_lcdc_free_dma_callback();
673	clk_put(sossi.fck);
674	iounmap(sossi.base);
675}
676
677struct lcd_ctrl_extif omap1_ext_if = {
678	.init			= sossi_init,
679	.cleanup		= sossi_cleanup,
680	.get_clk_info		= sossi_get_clk_info,
681	.convert_timings	= sossi_convert_timings,
682	.set_timings		= sossi_set_timings,
683	.set_bits_per_cycle	= sossi_set_bits_per_cycle,
684	.setup_tearsync		= sossi_setup_tearsync,
685	.enable_tearsync	= sossi_enable_tearsync,
686	.write_command		= sossi_write_command,
687	.read_data		= sossi_read_data,
688	.write_data		= sossi_write_data,
689	.transfer_area		= sossi_transfer_area,
690
691	.max_transmit_size	= SOSSI_MAX_XMIT_BYTES,
692};
693