1 /* /linux/driver/video/samsung/s5p_fimd_lite.c
3 * Samsung SoC FIMD Lite driver.
5 * Author: InKi Dae <inki.dae@samsung.com>
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.
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.
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.
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>
28 #include <linux/irq.h>
29 #include <linux/slab.h>
30 #include <linux/interrupt.h>
33 #include <plat/clock.h>
36 #include <plat/fimd_lite_ext.h>
37 #include <plat/regs-fb.h>
41 #include "s5p_fimd_ext.h"
42 #include "s5p_fimd_lite.h"
43 #include "regs_fimd_lite.h"
45 static void *to_fimd_lite_platform_data(struct s5p_fimd_ext_device *fx_dev)
47 return fx_dev->dev.platform_data ? (void *)fx_dev->dev.platform_data :
51 static void s5p_fimd_lite_set_par(struct s5p_fimd_lite *fimd_lite,
54 struct exynos_drm_fimd_pdata *lcd;
55 struct fb_videomode timing;
59 timing = lcd->panel.timing;
61 /* set window control */
62 cfg = readl(fimd_lite->iomem_base + S5P_WINCON(win_id));
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);
69 /* DATAPATH is LOCAL */
70 cfg |= S5P_WINCON_DATAPATH_LOCAL;
72 /* pixel format is unpacked RGB888 */
73 cfg |= S5P_WINCON_INRGB_RGB | S5P_WINCON_BPPMODE_32BPP;
75 writel(cfg, fimd_lite->iomem_base + S5P_WINCON(win_id));
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));
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));
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));
92 static void s5p_fimd_lite_set_clock(struct s5p_fimd_lite *fimd_lite)
94 unsigned int cfg = 0, div = 0;
95 unsigned int pixel_clock, src_clock, max_clock;
97 struct exynos_drm_fimd_pdata *lcd;
98 struct fb_videomode timing;
100 lcd = fimd_lite->lcd;
101 timing = lcd->panel.timing;
103 clk = fimd_lite->clk;
105 max_clock = 86 * 1000000;
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);
112 src_clock = clk_get_rate(clk->parent);
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);
119 cfg |= S5P_VIDCON0_CLKSEL_HCLK;
121 if (pixel_clock > max_clock)
122 pixel_clock = max_clock;
124 div = (unsigned int)(src_clock / pixel_clock);
125 if (src_clock % pixel_clock)
128 cfg |= S5P_VIDCON0_CLKVAL_F(div - 1);
129 writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
134 static void s5p_fimd_lite_window_on(struct s5p_fimd_lite *fimd_lite,
135 unsigned int win_id, unsigned int enable)
140 cfg = readl(fimd_lite->iomem_base + S5P_WINCON(win_id));
142 cfg &= ~S5P_WINCON_ENWIN_ENABLE;
145 cfg |= S5P_WINCON_ENWIN_ENABLE;
147 writel(cfg, fimd_lite->iomem_base + S5P_WINCON(win_id));
150 static void s5p_fimd_lite_lcd_on(struct s5p_fimd_lite *fimd_lite,
155 cfg = readl(fimd_lite->iomem_base + S5P_VIDCON0);
157 cfg &= ~(S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
160 cfg |= (S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
162 writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
165 void s5p_fimd_lite_get_vsync_interrupt(struct s5p_fimd_lite *fimd_lite,
170 cfg = readl(fimd_lite->iomem_base + S5P_VIDINTCON0);
171 cfg &= ~(S5P_VIDINTCON0_INTFRMEN_ENABLE | S5P_VIDINTCON0_INT_ENABLE |
172 S5P_VIDINTCON0_FRAMESEL0_VSYNC);
175 cfg |= (S5P_VIDINTCON0_INTFRMEN_ENABLE |
176 S5P_VIDINTCON0_INT_ENABLE |
177 S5P_VIDINTCON0_FRAMESEL0_VSYNC);
179 cfg |= (S5P_VIDINTCON0_INTFRMEN_DISABLE |
180 S5P_VIDINTCON0_INT_DISABLE);
182 cfg &= ~S5P_VIDINTCON0_FRAMESEL0_VSYNC;
185 writel(cfg, fimd_lite->iomem_base + S5P_VIDINTCON0);
188 static irqreturn_t s5p_fimd_lite_irq_frame(int irq, void *dev_id)
190 struct s5p_fimd_lite *fimd_lite;
192 fimd_lite = (struct s5p_fimd_lite *)dev_id;
194 disable_irq_nosync(fimd_lite->irq);
196 enable_irq(fimd_lite->irq);
201 static void s5p_fimd_lite_logic_start(struct s5p_fimd_lite *fimd_lite,
209 writel(cfg, fimd_lite->iomem_base + S5P_GPOUTCON0);
211 writel(0, fimd_lite->iomem_base + S5P_GPOUTCON0);
214 static void s5p_fimd_lite_lcd_init(struct s5p_fimd_lite *fimd_lite)
216 unsigned int cfg, rgb_mode, win_id = 0;
217 struct exynos_drm_fimd_pdata *lcd;
218 struct fb_videomode timing;
220 lcd = fimd_lite->lcd;
221 timing = lcd->panel.timing;
226 writel(cfg, fimd_lite->iomem_base + S5P_VIDCON1);
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);
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);
240 writel(cfg, fimd_lite->iomem_base + S5P_VIDTCON1);
244 cfg |= S5P_VIDTCON2_HOZVAL(timing.xres - 1);
245 cfg |= S5P_VIDTCON2_LINEVAL(timing.yres - 1);
247 writel(cfg, fimd_lite->iomem_base + S5P_VIDTCON2);
249 writel(0, fimd_lite->iomem_base + S5P_DITHMODE);
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;
256 cfg |= S5P_VIDCON0_VIDOUT_RGB;
257 writel(cfg, fimd_lite->iomem_base + S5P_VIDCON0);
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);
265 s5p_fimd_lite_get_vsync_interrupt(fimd_lite, 0);
268 s5p_fimd_lite_set_par(fimd_lite, win_id);
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));
275 s5p_fimd_lite_set_clock(fimd_lite);
280 static int s5p_fimd_lite_setup(struct s5p_fimd_ext_device *fx_dev,
283 struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
285 s5p_fimd_lite_logic_start(fimd_lite, enable);
287 s5p_fimd_lite_lcd_init(fimd_lite);
290 s5p_fimd_lite_window_on(fimd_lite, 0, 1);
295 static int s5p_fimd_lite_start(struct s5p_fimd_ext_device *fx_dev)
297 struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
299 s5p_fimd_lite_lcd_on(fimd_lite, 1);
304 static void s5p_fimd_lite_stop(struct s5p_fimd_ext_device *fx_dev)
306 struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
308 s5p_fimd_lite_lcd_on(fimd_lite, 0);
311 static void s5p_fimd_lite_power_on(struct s5p_fimd_ext_device *fx_dev)
313 struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
315 clk_enable(fimd_lite->clk);
318 static void s5p_fimd_lite_power_off(struct s5p_fimd_ext_device *fx_dev)
320 struct s5p_fimd_lite *fimd_lite = fimd_ext_get_drvdata(fx_dev);
322 clk_disable(fimd_lite->clk);
325 static int s5p_fimd_lite_probe(struct s5p_fimd_ext_device *fx_dev)
327 struct clk *sclk = NULL;
328 struct resource *res;
329 struct s5p_fimd_lite *fimd_lite;
332 fimd_lite = kzalloc(sizeof(struct s5p_fimd_lite), GFP_KERNEL);
334 dev_err(&fx_dev->dev, "failed to alloc fimd_lite object.\n");
338 fimd_lite->dev = &fx_dev->dev;
339 fimd_lite->lcd = (struct exynos_drm_fimd_pdata *)
340 to_fimd_lite_platform_data(fx_dev);
342 res = s5p_fimd_ext_get_resource(fx_dev, IORESOURCE_MEM, 0);
344 dev_err(&fx_dev->dev, "failed to get io memory region.\n");
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");
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");
363 sclk = clk_get(&fx_dev->dev, "sclk_mdnie");
365 dev_err(&fx_dev->dev, "failed to get sclk_mdnie clock\n");
369 fimd_lite->clk->parent = sclk;
371 fimd_lite->irq = s5p_fimd_ext_get_irq(fx_dev, 0);
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");
381 fimd_ext_set_drvdata(fx_dev, fimd_lite);
383 dev_info(&fx_dev->dev, "fimd lite driver has been probed.\n");
388 free_irq(fimd_lite->irq, fx_dev);
392 iounmap(fimd_lite->iomem_base);
393 clk_put(fimd_lite->clk);
401 static struct s5p_fimd_ext_driver fimd_ext_driver = {
404 .owner = THIS_MODULE,
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,
414 static int __init s5p_fimd_lite_init(void)
416 return s5p_fimd_ext_driver_register(&fimd_ext_driver);
419 static void __exit s5p_fimd_lite_exit(void)
423 arch_initcall(s5p_fimd_lite_init);
424 module_exit(s5p_fimd_lite_exit);
426 MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
427 MODULE_DESCRIPTION("FIMD Lite Driver");
428 MODULE_LICENSE("GPL");