Initial commit
[kernel/linux-3.0.git] / drivers / video / samsung_duallcd / extension / s5p_fimd_lite.c
1 /* /linux/driver/video/samsung/s5p_fimd_lite.c
2  *
3  * Samsung SoC FIMD Lite driver.
4  *
5  * Author: InKi Dae  <inki.dae@samsung.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
22 #include <linux/module.h>
23 #include <linux/kernel.h>
24 #include <linux/errno.h>
25 #include <linux/err.h>
26 #include <linux/clk.h>
27 #include <linux/io.h>
28 #include <linux/irq.h>
29 #include <linux/slab.h>
30 #include <linux/interrupt.h>
31 #include <linux/fb.h>
32
33 #include <plat/clock.h>
34 #include <plat/fb.h>
35 #include <plat/cpu.h>
36 #include <plat/fimd_lite_ext.h>
37 #include <plat/regs-fb.h>
38
39 #include <mach/map.h>
40
41 #include "s5p_fimd_ext.h"
42 #include "s5p_fimd_lite.h"
43 #include "regs_fimd_lite.h"
44
45 static void *to_fimd_lite_platform_data(struct s5p_fimd_ext_device *fx_dev)
46 {
47         return fx_dev->dev.platform_data ? (void *)fx_dev->dev.platform_data :
48                                                 NULL;
49 }
50
51 static void s5p_fimd_lite_set_par(struct s5p_fimd_lite *fimd_lite,
52         unsigned int win_id)
53 {
54         struct exynos_drm_fimd_pdata *lcd;
55         struct fb_videomode timing;
56         unsigned int cfg;
57
58         lcd = fimd_lite->lcd;
59         timing = lcd->panel.timing;
60
61         /* set window control */
62         cfg = readl(fimd_lite->iomem_base + S5P_WINCON(win_id));
63
64         cfg &= ~(S5P_WINCON_BITSWP_ENABLE | S5P_WINCON_BYTESWP_ENABLE | \
65                 S5P_WINCON_HAWSWP_ENABLE | S5P_WINCON_WSWP_ENABLE | \
66                 S5P_WINCON_BURSTLEN_MASK | S5P_WINCON_BPPMODE_MASK | \
67                 S5P_WINCON_INRGB_MASK | S5P_WINCON_DATAPATH_MASK);
68
69         /* DATAPATH is LOCAL */
70         cfg |= S5P_WINCON_DATAPATH_LOCAL;
71
72         /* pixel format is unpacked RGB888 */
73         cfg |= S5P_WINCON_INRGB_RGB | S5P_WINCON_BPPMODE_32BPP;
74
75         writel(cfg, fimd_lite->iomem_base + S5P_WINCON(win_id));
76
77         /* set window position to x=0, y=0*/
78         cfg = S5P_VIDOSD_LEFT_X(0) | S5P_VIDOSD_TOP_Y(0);
79         writel(cfg, fimd_lite->iomem_base + S5P_VIDOSD_A(win_id));
80
81         cfg = S5P_VIDOSD_RIGHT_X(timing.xres - 1) |
82                 S5P_VIDOSD_BOTTOM_Y(timing.yres - 1);
83         writel(cfg, fimd_lite->iomem_base + S5P_VIDOSD_B(win_id));
84
85         /* set window size for window0*/
86         cfg = S5P_VIDOSD_SIZE(timing.xres * timing.yres);
87         writel(cfg, fimd_lite->iomem_base + S5P_VIDOSD_C(win_id));
88
89         return;
90 }
91
92 static void s5p_fimd_lite_set_clock(struct s5p_fimd_lite *fimd_lite)
93 {
94         unsigned int cfg = 0, div = 0;
95         unsigned int pixel_clock, src_clock, max_clock;
96         struct clk *clk;
97         struct exynos_drm_fimd_pdata *lcd;
98         struct fb_videomode timing;
99
100         lcd = fimd_lite->lcd;
101         timing = lcd->panel.timing;
102
103         clk = fimd_lite->clk;
104
105         max_clock = 86 * 1000000;
106
107         pixel_clock = timing.refresh *
108                 (timing.left_margin + timing.right_margin +
109                 timing.hsync_len + timing.xres) * (timing.upper_margin +
110                 timing.lower_margin + timing.vsync_len + timing.yres);
111
112         src_clock = clk_get_rate(clk->parent);
113
114         cfg = readl(fimd_lite->iomem_base + S5P_VIDCON0);
115         cfg &= ~(S5P_VIDCON0_VCLKEN_MASK | S5P_VIDCON0_CLKVALUP_MASK |
116                  S5P_VIDCON0_CLKVAL_F(0xFF));
117         cfg |= (S5P_VIDCON0_CLKVALUP_ALWAYS | S5P_VIDCON0_VCLKEN_NORMAL);
118
119         cfg |= S5P_VIDCON0_CLKSEL_HCLK;
120
121         if (pixel_clock > max_clock)
122                 pixel_clock = max_clock;
123
124         div = (unsigned int)(src_clock / pixel_clock);
125         if (src_clock % pixel_clock)
126                 div++;
127
128         cfg |= S5P_VIDCON0_CLKVAL_F(div - 1);
129         writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
130
131         return;
132 }
133
134 static void s5p_fimd_lite_window_on(struct s5p_fimd_lite *fimd_lite,
135                 unsigned int win_id, unsigned int enable)
136 {
137         unsigned int cfg;
138
139         /* enable window */
140         cfg = readl(fimd_lite->iomem_base + S5P_WINCON(win_id));
141
142         cfg &= ~S5P_WINCON_ENWIN_ENABLE;
143
144         if (enable)
145                 cfg |= S5P_WINCON_ENWIN_ENABLE;
146
147         writel(cfg, fimd_lite->iomem_base + S5P_WINCON(win_id));
148 }
149
150 static void s5p_fimd_lite_lcd_on(struct s5p_fimd_lite *fimd_lite,
151                 unsigned int enable)
152 {
153         unsigned int cfg;
154
155         cfg = readl(fimd_lite->iomem_base + S5P_VIDCON0);
156
157         cfg &= ~(S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
158
159         if (enable)
160                 cfg |= (S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
161
162         writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
163 }
164
165 void s5p_fimd_lite_get_vsync_interrupt(struct s5p_fimd_lite *fimd_lite,
166         unsigned int enable)
167 {
168         unsigned int cfg;
169
170         cfg = readl(fimd_lite->iomem_base + S5P_VIDINTCON0);
171         cfg &= ~(S5P_VIDINTCON0_INTFRMEN_ENABLE | S5P_VIDINTCON0_INT_ENABLE |
172                 S5P_VIDINTCON0_FRAMESEL0_VSYNC);
173
174         if (enable) {
175                 cfg |= (S5P_VIDINTCON0_INTFRMEN_ENABLE |
176                         S5P_VIDINTCON0_INT_ENABLE |
177                         S5P_VIDINTCON0_FRAMESEL0_VSYNC);
178         } else {
179                 cfg |= (S5P_VIDINTCON0_INTFRMEN_DISABLE |
180                         S5P_VIDINTCON0_INT_DISABLE);
181
182                 cfg &= ~S5P_VIDINTCON0_FRAMESEL0_VSYNC;
183         }
184
185         writel(cfg, fimd_lite->iomem_base + S5P_VIDINTCON0);
186 }
187
188 static irqreturn_t s5p_fimd_lite_irq_frame(int irq, void *dev_id)
189 {
190         struct s5p_fimd_lite *fimd_lite;
191
192         fimd_lite = (struct s5p_fimd_lite *)dev_id;
193
194         disable_irq_nosync(fimd_lite->irq);
195
196         enable_irq(fimd_lite->irq);
197
198         return IRQ_HANDLED;
199 }
200
201 static void s5p_fimd_lite_logic_start(struct s5p_fimd_lite *fimd_lite,
202                         unsigned int enable)
203 {
204         unsigned int cfg;
205
206         cfg = 0x2ff47;
207
208         if (enable)
209                 writel(cfg, fimd_lite->iomem_base + S5P_GPOUTCON0);
210         else
211                 writel(0, fimd_lite->iomem_base + S5P_GPOUTCON0);
212 }
213
214 static void s5p_fimd_lite_lcd_init(struct s5p_fimd_lite *fimd_lite)
215 {
216         unsigned int cfg, rgb_mode, win_id = 0;
217         struct exynos_drm_fimd_pdata *lcd;
218         struct fb_videomode timing;
219
220         lcd = fimd_lite->lcd;
221         timing = lcd->panel.timing;
222
223         cfg = 0;
224         cfg |= lcd->vidcon1;
225
226         writel(cfg, fimd_lite->iomem_base + S5P_VIDCON1);
227
228         /* set timing */
229         cfg = 0;
230         cfg |= S5P_VIDTCON0_VBPD(timing.upper_margin - 1);
231         cfg |= S5P_VIDTCON0_VFPD(timing.lower_margin - 1);
232         cfg |= S5P_VIDTCON0_VSPW(timing.vsync_len - 1);
233         writel(cfg, fimd_lite->iomem_base + S5P_VIDTCON0);
234
235         cfg = 0;
236         cfg |= S5P_VIDTCON1_HBPD(timing.left_margin - 1);
237         cfg |= S5P_VIDTCON1_HFPD(timing.right_margin - 1);
238         cfg |= S5P_VIDTCON1_HSPW(timing.hsync_len - 1);
239
240         writel(cfg, fimd_lite->iomem_base + S5P_VIDTCON1);
241
242         /* set lcd size */
243         cfg = 0;
244         cfg |= S5P_VIDTCON2_HOZVAL(timing.xres - 1);
245         cfg |= S5P_VIDTCON2_LINEVAL(timing.yres - 1);
246
247         writel(cfg, fimd_lite->iomem_base + S5P_VIDTCON2);
248
249         writel(0, fimd_lite->iomem_base + S5P_DITHMODE);
250
251         /* set output to RGB */
252         rgb_mode = 0; /* MODE_RGB_P */
253         cfg = readl(fimd_lite->iomem_base + S5P_VIDCON0);
254         cfg &= ~S5P_VIDCON0_VIDOUT_MASK;
255
256         cfg |= S5P_VIDCON0_VIDOUT_RGB;
257         writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
258
259         /* set display mode */
260         cfg = readl(fimd_lite->iomem_base + S5P_VIDCON0);
261         cfg &= ~S5P_VIDCON0_PNRMODE_MASK;
262         cfg |= (rgb_mode << S5P_VIDCON0_PNRMODE_SHIFT);
263         writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
264
265         s5p_fimd_lite_get_vsync_interrupt(fimd_lite, 0);
266
267         /* set par */
268         s5p_fimd_lite_set_par(fimd_lite, win_id);
269
270         /* set buffer size */
271         cfg = S5P_VIDADDR_PAGEWIDTH(timing.xres * lcd->bpp / 8);
272         writel(cfg, fimd_lite->iomem_base + S5P_VIDADDR_SIZE(win_id));
273
274         /* set clock */
275         s5p_fimd_lite_set_clock(fimd_lite);
276
277         return;
278 }
279
280 static int s5p_fimd_lite_setup(struct s5p_fimd_ext_device *fx_dev,
281                                 unsigned int enable)
282 {
283         struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
284
285         s5p_fimd_lite_logic_start(fimd_lite, enable);
286
287         s5p_fimd_lite_lcd_init(fimd_lite);
288
289
290         s5p_fimd_lite_window_on(fimd_lite, 0, 1);
291
292         return 0;
293 }
294
295 static int s5p_fimd_lite_start(struct s5p_fimd_ext_device *fx_dev)
296 {
297         struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
298
299         s5p_fimd_lite_lcd_on(fimd_lite, 1);
300
301         return 0;
302 }
303
304 static void s5p_fimd_lite_stop(struct s5p_fimd_ext_device *fx_dev)
305 {
306         struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
307
308         s5p_fimd_lite_lcd_on(fimd_lite, 0);
309 }
310
311 static void s5p_fimd_lite_power_on(struct s5p_fimd_ext_device *fx_dev)
312 {
313         struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
314
315         clk_enable(fimd_lite->clk);
316 }
317
318 static void s5p_fimd_lite_power_off(struct s5p_fimd_ext_device *fx_dev)
319 {
320         struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
321
322         clk_disable(fimd_lite->clk);
323 }
324
325 static int s5p_fimd_lite_probe(struct s5p_fimd_ext_device *fx_dev)
326 {
327         struct clk *sclk = NULL;
328         struct resource *res;
329         struct s5p_fimd_lite *fimd_lite;
330         int ret = -1;
331
332         fimd_lite = kzalloc(sizeof(struct s5p_fimd_lite), GFP_KERNEL);
333         if (!fimd_lite) {
334                 dev_err(&fx_dev->dev, "failed to alloc fimd_lite object.\n");
335                 return -EFAULT;
336         }
337
338         fimd_lite->dev = &fx_dev->dev;
339         fimd_lite->lcd = (struct exynos_drm_fimd_pdata *)
340                         to_fimd_lite_platform_data(fx_dev);
341
342         res = s5p_fimd_ext_get_resource(fx_dev, IORESOURCE_MEM, 0);
343         if (!res) {
344                 dev_err(&fx_dev->dev, "failed to get io memory region.\n");
345                 ret = -EINVAL;
346                 goto err0;
347         }
348
349         fimd_lite->iomem_base = ioremap(res->start, resource_size(res));
350         if (!fimd_lite->iomem_base) {
351                 dev_err(&fx_dev->dev, "failed to remap io region\n");
352                 ret = -EFAULT;
353                 goto err0;
354         }
355
356         fimd_lite->clk = clk_get(&fx_dev->dev, "mdnie0");
357         if (IS_ERR(fimd_lite->clk)) {
358                 dev_err(&fx_dev->dev, "failed to get FIMD LITE clock source\n");
359                 ret = -EINVAL;
360                 goto err1;
361         }
362
363         sclk = clk_get(&fx_dev->dev, "sclk_mdnie");
364         if (IS_ERR(sclk)) {
365                 dev_err(&fx_dev->dev, "failed to get sclk_mdnie clock\n");
366                 ret = -EINVAL;
367                 goto err2;
368         }
369         fimd_lite->clk->parent = sclk;
370
371         fimd_lite->irq = s5p_fimd_ext_get_irq(fx_dev, 0);
372
373         /* register interrupt handler for fimd-lite. */
374         if (request_irq(fimd_lite->irq, s5p_fimd_lite_irq_frame, IRQF_DISABLED,
375                 fx_dev->name, (void *)fimd_lite)) {
376                 dev_err(&fx_dev->dev, "request_irq failed\n");
377                 ret = -EINVAL;
378                 goto err3;
379         }
380
381         fimd_ext_set_drvdata(fx_dev, fimd_lite);
382
383         dev_info(&fx_dev->dev, "fimd lite driver has been probed.\n");
384
385         return 0;
386
387 err3:
388         free_irq(fimd_lite->irq, fx_dev);
389 err2:
390         clk_put(sclk);
391 err1:
392         iounmap(fimd_lite->iomem_base);
393         clk_put(fimd_lite->clk);
394 err0:
395         kfree(fimd_lite);
396
397         return ret;
398
399 }
400
401 static struct s5p_fimd_ext_driver fimd_ext_driver = {
402         .driver         = {
403                 .name   = "fimd_lite",
404                 .owner  = THIS_MODULE,
405         },
406         .power_on       = s5p_fimd_lite_power_on,
407         .power_off      = s5p_fimd_lite_power_off,
408         .setup          = s5p_fimd_lite_setup,
409         .start          = s5p_fimd_lite_start,
410         .stop           = s5p_fimd_lite_stop,
411         .probe          = s5p_fimd_lite_probe,
412 };
413
414 static int __init s5p_fimd_lite_init(void)
415 {
416         return s5p_fimd_ext_driver_register(&fimd_ext_driver);
417 }
418
419 static void __exit s5p_fimd_lite_exit(void)
420 {
421 }
422
423 arch_initcall(s5p_fimd_lite_init);
424 module_exit(s5p_fimd_lite_exit);
425
426 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
427 MODULE_DESCRIPTION("FIMD Lite Driver");
428 MODULE_LICENSE("GPL");
429