diff options
Diffstat (limited to 'drivers/media/video/s5p-tv')
-rw-r--r-- | drivers/media/video/s5p-tv/Kconfig | 76 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/Makefile | 17 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/hdmi_drv.c | 1042 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/hdmiphy_drv.c | 188 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer.h | 354 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_drv.c | 487 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_grp_layer.c | 185 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_reg.c | 541 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_video.c | 1006 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/mixer_vp_layer.c | 211 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/regs-hdmi.h | 141 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/regs-mixer.h | 121 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/regs-sdo.h | 63 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/regs-vp.h | 88 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/sdo_drv.c | 479 |
15 files changed, 4999 insertions, 0 deletions
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig new file mode 100644 index 00000000000..9c37dee7bc5 --- /dev/null +++ b/drivers/media/video/s5p-tv/Kconfig | |||
@@ -0,0 +1,76 @@ | |||
1 | # drivers/media/video/s5p-tv/Kconfig | ||
2 | # | ||
3 | # Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | # http://www.samsung.com/ | ||
5 | # Tomasz Stanislawski <t.stanislaws@samsung.com> | ||
6 | # | ||
7 | # Licensed under GPL | ||
8 | |||
9 | config VIDEO_SAMSUNG_S5P_TV | ||
10 | bool "Samsung TV driver for S5P platform (experimental)" | ||
11 | depends on PLAT_S5P | ||
12 | depends on EXPERIMENTAL | ||
13 | default n | ||
14 | ---help--- | ||
15 | Say Y here to enable selecting the TV output devices for | ||
16 | Samsung S5P platform. | ||
17 | |||
18 | if VIDEO_SAMSUNG_S5P_TV | ||
19 | |||
20 | config VIDEO_SAMSUNG_S5P_HDMI | ||
21 | tristate "Samsung HDMI Driver" | ||
22 | depends on VIDEO_V4L2 | ||
23 | depends on VIDEO_SAMSUNG_S5P_TV | ||
24 | select VIDEO_SAMSUNG_S5P_HDMIPHY | ||
25 | help | ||
26 | Say Y here if you want support for the HDMI output | ||
27 | interface in S5P Samsung SoC. The driver can be compiled | ||
28 | as module. It is an auxiliary driver, that exposes a V4L2 | ||
29 | subdev for use by other drivers. This driver requires | ||
30 | hdmiphy driver to work correctly. | ||
31 | |||
32 | config VIDEO_SAMSUNG_S5P_HDMI_DEBUG | ||
33 | bool "Enable debug for HDMI Driver" | ||
34 | depends on VIDEO_SAMSUNG_S5P_HDMI | ||
35 | default n | ||
36 | help | ||
37 | Enables debugging for HDMI driver. | ||
38 | |||
39 | config VIDEO_SAMSUNG_S5P_HDMIPHY | ||
40 | tristate "Samsung HDMIPHY Driver" | ||
41 | depends on VIDEO_DEV && VIDEO_V4L2 && I2C | ||
42 | depends on VIDEO_SAMSUNG_S5P_TV | ||
43 | help | ||
44 | Say Y here if you want support for the physical HDMI | ||
45 | interface in S5P Samsung SoC. The driver can be compiled | ||
46 | as module. It is an I2C driver, that exposes a V4L2 | ||
47 | subdev for use by other drivers. | ||
48 | |||
49 | config VIDEO_SAMSUNG_S5P_SDO | ||
50 | tristate "Samsung Analog TV Driver" | ||
51 | depends on VIDEO_DEV && VIDEO_V4L2 | ||
52 | depends on VIDEO_SAMSUNG_S5P_TV | ||
53 | help | ||
54 | Say Y here if you want support for the analog TV output | ||
55 | interface in S5P Samsung SoC. The driver can be compiled | ||
56 | as module. It is an auxiliary driver, that exposes a V4L2 | ||
57 | subdev for use by other drivers. This driver requires | ||
58 | hdmiphy driver to work correctly. | ||
59 | |||
60 | config VIDEO_SAMSUNG_S5P_MIXER | ||
61 | tristate "Samsung Mixer and Video Processor Driver" | ||
62 | depends on VIDEO_DEV && VIDEO_V4L2 | ||
63 | depends on VIDEO_SAMSUNG_S5P_TV | ||
64 | select VIDEOBUF2_DMA_CONTIG | ||
65 | help | ||
66 | Say Y here if you want support for the Mixer in Samsung S5P SoCs. | ||
67 | This device produce image data to one of output interfaces. | ||
68 | |||
69 | config VIDEO_SAMSUNG_S5P_MIXER_DEBUG | ||
70 | bool "Enable debug for Mixer Driver" | ||
71 | depends on VIDEO_SAMSUNG_S5P_MIXER | ||
72 | default n | ||
73 | help | ||
74 | Enables debugging for Mixer driver. | ||
75 | |||
76 | endif # VIDEO_SAMSUNG_S5P_TV | ||
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile new file mode 100644 index 00000000000..37e4c17663b --- /dev/null +++ b/drivers/media/video/s5p-tv/Makefile | |||
@@ -0,0 +1,17 @@ | |||
1 | # drivers/media/video/samsung/tvout/Makefile | ||
2 | # | ||
3 | # Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | # http://www.samsung.com/ | ||
5 | # Tomasz Stanislawski <t.stanislaws@samsung.com> | ||
6 | # | ||
7 | # Licensed under GPL | ||
8 | |||
9 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o | ||
10 | s5p-hdmiphy-y += hdmiphy_drv.o | ||
11 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o | ||
12 | s5p-hdmi-y += hdmi_drv.o | ||
13 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o | ||
14 | s5p-sdo-y += sdo_drv.o | ||
15 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o | ||
16 | s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o | ||
17 | |||
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c new file mode 100644 index 00000000000..06d6663f459 --- /dev/null +++ b/drivers/media/video/s5p-tv/hdmi_drv.c | |||
@@ -0,0 +1,1042 @@ | |||
1 | /* | ||
2 | * Samsung HDMI interface driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG | ||
15 | #define DEBUG | ||
16 | #endif | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <media/v4l2-subdev.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/bug.h> | ||
29 | #include <linux/pm_runtime.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/regulator/consumer.h> | ||
32 | |||
33 | #include <media/v4l2-common.h> | ||
34 | #include <media/v4l2-dev.h> | ||
35 | #include <media/v4l2-device.h> | ||
36 | |||
37 | #include "regs-hdmi.h" | ||
38 | |||
39 | MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); | ||
40 | MODULE_DESCRIPTION("Samsung HDMI"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | /* default preset configured on probe */ | ||
44 | #define HDMI_DEFAULT_PRESET V4L2_DV_1080P60 | ||
45 | |||
46 | struct hdmi_resources { | ||
47 | struct clk *hdmi; | ||
48 | struct clk *sclk_hdmi; | ||
49 | struct clk *sclk_pixel; | ||
50 | struct clk *sclk_hdmiphy; | ||
51 | struct clk *hdmiphy; | ||
52 | struct regulator_bulk_data *regul_bulk; | ||
53 | int regul_count; | ||
54 | }; | ||
55 | |||
56 | struct hdmi_device { | ||
57 | /** base address of HDMI registers */ | ||
58 | void __iomem *regs; | ||
59 | /** HDMI interrupt */ | ||
60 | unsigned int irq; | ||
61 | /** pointer to device parent */ | ||
62 | struct device *dev; | ||
63 | /** subdev generated by HDMI device */ | ||
64 | struct v4l2_subdev sd; | ||
65 | /** V4L2 device structure */ | ||
66 | struct v4l2_device v4l2_dev; | ||
67 | /** subdev of HDMIPHY interface */ | ||
68 | struct v4l2_subdev *phy_sd; | ||
69 | /** configuration of current graphic mode */ | ||
70 | const struct hdmi_preset_conf *cur_conf; | ||
71 | /** current preset */ | ||
72 | u32 cur_preset; | ||
73 | /** other resources */ | ||
74 | struct hdmi_resources res; | ||
75 | }; | ||
76 | |||
77 | struct hdmi_driver_data { | ||
78 | int hdmiphy_bus; | ||
79 | }; | ||
80 | |||
81 | struct hdmi_tg_regs { | ||
82 | u8 cmd; | ||
83 | u8 h_fsz_l; | ||
84 | u8 h_fsz_h; | ||
85 | u8 hact_st_l; | ||
86 | u8 hact_st_h; | ||
87 | u8 hact_sz_l; | ||
88 | u8 hact_sz_h; | ||
89 | u8 v_fsz_l; | ||
90 | u8 v_fsz_h; | ||
91 | u8 vsync_l; | ||
92 | u8 vsync_h; | ||
93 | u8 vsync2_l; | ||
94 | u8 vsync2_h; | ||
95 | u8 vact_st_l; | ||
96 | u8 vact_st_h; | ||
97 | u8 vact_sz_l; | ||
98 | u8 vact_sz_h; | ||
99 | u8 field_chg_l; | ||
100 | u8 field_chg_h; | ||
101 | u8 vact_st2_l; | ||
102 | u8 vact_st2_h; | ||
103 | u8 vsync_top_hdmi_l; | ||
104 | u8 vsync_top_hdmi_h; | ||
105 | u8 vsync_bot_hdmi_l; | ||
106 | u8 vsync_bot_hdmi_h; | ||
107 | u8 field_top_hdmi_l; | ||
108 | u8 field_top_hdmi_h; | ||
109 | u8 field_bot_hdmi_l; | ||
110 | u8 field_bot_hdmi_h; | ||
111 | }; | ||
112 | |||
113 | struct hdmi_core_regs { | ||
114 | u8 h_blank[2]; | ||
115 | u8 v_blank[3]; | ||
116 | u8 h_v_line[3]; | ||
117 | u8 vsync_pol[1]; | ||
118 | u8 int_pro_mode[1]; | ||
119 | u8 v_blank_f[3]; | ||
120 | u8 h_sync_gen[3]; | ||
121 | u8 v_sync_gen1[3]; | ||
122 | u8 v_sync_gen2[3]; | ||
123 | u8 v_sync_gen3[3]; | ||
124 | }; | ||
125 | |||
126 | struct hdmi_preset_conf { | ||
127 | struct hdmi_core_regs core; | ||
128 | struct hdmi_tg_regs tg; | ||
129 | struct v4l2_mbus_framefmt mbus_fmt; | ||
130 | }; | ||
131 | |||
132 | /* I2C module and id for HDMIPHY */ | ||
133 | static struct i2c_board_info hdmiphy_info = { | ||
134 | I2C_BOARD_INFO("hdmiphy", 0x38), | ||
135 | }; | ||
136 | |||
137 | static struct hdmi_driver_data hdmi_driver_data[] = { | ||
138 | { .hdmiphy_bus = 3 }, | ||
139 | { .hdmiphy_bus = 8 }, | ||
140 | }; | ||
141 | |||
142 | static struct platform_device_id hdmi_driver_types[] = { | ||
143 | { | ||
144 | .name = "s5pv210-hdmi", | ||
145 | .driver_data = (unsigned long)&hdmi_driver_data[0], | ||
146 | }, { | ||
147 | .name = "exynos4-hdmi", | ||
148 | .driver_data = (unsigned long)&hdmi_driver_data[1], | ||
149 | }, { | ||
150 | /* end node */ | ||
151 | } | ||
152 | }; | ||
153 | |||
154 | static const struct v4l2_subdev_ops hdmi_sd_ops; | ||
155 | |||
156 | static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd) | ||
157 | { | ||
158 | return container_of(sd, struct hdmi_device, sd); | ||
159 | } | ||
160 | |||
161 | static inline | ||
162 | void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value) | ||
163 | { | ||
164 | writel(value, hdev->regs + reg_id); | ||
165 | } | ||
166 | |||
167 | static inline | ||
168 | void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask) | ||
169 | { | ||
170 | u32 old = readl(hdev->regs + reg_id); | ||
171 | value = (value & mask) | (old & ~mask); | ||
172 | writel(value, hdev->regs + reg_id); | ||
173 | } | ||
174 | |||
175 | static inline | ||
176 | void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) | ||
177 | { | ||
178 | writeb(value, hdev->regs + reg_id); | ||
179 | } | ||
180 | |||
181 | static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) | ||
182 | { | ||
183 | return readl(hdev->regs + reg_id); | ||
184 | } | ||
185 | |||
186 | static irqreturn_t hdmi_irq_handler(int irq, void *dev_data) | ||
187 | { | ||
188 | struct hdmi_device *hdev = dev_data; | ||
189 | u32 intc_flag; | ||
190 | |||
191 | (void)irq; | ||
192 | intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG); | ||
193 | /* clearing flags for HPD plug/unplug */ | ||
194 | if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) { | ||
195 | printk(KERN_INFO "unplugged\n"); | ||
196 | hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, | ||
197 | HDMI_INTC_FLAG_HPD_UNPLUG); | ||
198 | } | ||
199 | if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) { | ||
200 | printk(KERN_INFO "plugged\n"); | ||
201 | hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0, | ||
202 | HDMI_INTC_FLAG_HPD_PLUG); | ||
203 | } | ||
204 | |||
205 | return IRQ_HANDLED; | ||
206 | } | ||
207 | |||
208 | static void hdmi_reg_init(struct hdmi_device *hdev) | ||
209 | { | ||
210 | /* enable HPD interrupts */ | ||
211 | hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL | | ||
212 | HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG); | ||
213 | /* choose HDMI mode */ | ||
214 | hdmi_write_mask(hdev, HDMI_MODE_SEL, | ||
215 | HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); | ||
216 | /* disable bluescreen */ | ||
217 | hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); | ||
218 | /* choose bluescreen (fecal) color */ | ||
219 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12); | ||
220 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34); | ||
221 | hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56); | ||
222 | /* enable AVI packet every vsync, fixes purple line problem */ | ||
223 | hdmi_writeb(hdev, HDMI_AVI_CON, 0x02); | ||
224 | /* force YUV444, look to CEA-861-D, table 7 for more detail */ | ||
225 | hdmi_writeb(hdev, HDMI_AVI_BYTE(0), 2 << 5); | ||
226 | hdmi_write_mask(hdev, HDMI_CON_1, 2, 3 << 5); | ||
227 | } | ||
228 | |||
229 | static void hdmi_timing_apply(struct hdmi_device *hdev, | ||
230 | const struct hdmi_preset_conf *conf) | ||
231 | { | ||
232 | const struct hdmi_core_regs *core = &conf->core; | ||
233 | const struct hdmi_tg_regs *tg = &conf->tg; | ||
234 | |||
235 | /* setting core registers */ | ||
236 | hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]); | ||
237 | hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]); | ||
238 | hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]); | ||
239 | hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]); | ||
240 | hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]); | ||
241 | hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]); | ||
242 | hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]); | ||
243 | hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]); | ||
244 | hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]); | ||
245 | hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); | ||
246 | hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]); | ||
247 | hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]); | ||
248 | hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]); | ||
249 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); | ||
250 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); | ||
251 | hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); | ||
252 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); | ||
253 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); | ||
254 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); | ||
255 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); | ||
256 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); | ||
257 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); | ||
258 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); | ||
259 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); | ||
260 | hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); | ||
261 | /* Timing generator registers */ | ||
262 | hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l); | ||
263 | hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h); | ||
264 | hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l); | ||
265 | hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h); | ||
266 | hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); | ||
267 | hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); | ||
268 | hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l); | ||
269 | hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h); | ||
270 | hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l); | ||
271 | hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h); | ||
272 | hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l); | ||
273 | hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h); | ||
274 | hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l); | ||
275 | hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h); | ||
276 | hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); | ||
277 | hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); | ||
278 | hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); | ||
279 | hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); | ||
280 | hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); | ||
281 | hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); | ||
282 | hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); | ||
283 | hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); | ||
284 | hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); | ||
285 | hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); | ||
286 | hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); | ||
287 | hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); | ||
288 | hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); | ||
289 | hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); | ||
290 | } | ||
291 | |||
292 | static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) | ||
293 | { | ||
294 | struct device *dev = hdmi_dev->dev; | ||
295 | const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf; | ||
296 | struct v4l2_dv_preset preset; | ||
297 | int ret; | ||
298 | |||
299 | dev_dbg(dev, "%s\n", __func__); | ||
300 | |||
301 | /* reset hdmiphy */ | ||
302 | hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); | ||
303 | mdelay(10); | ||
304 | hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); | ||
305 | mdelay(10); | ||
306 | |||
307 | /* configure presets */ | ||
308 | preset.preset = hdmi_dev->cur_preset; | ||
309 | ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset); | ||
310 | if (ret) { | ||
311 | dev_err(dev, "failed to set preset (%u)\n", preset.preset); | ||
312 | return ret; | ||
313 | } | ||
314 | |||
315 | /* resetting HDMI core */ | ||
316 | hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); | ||
317 | mdelay(10); | ||
318 | hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); | ||
319 | mdelay(10); | ||
320 | |||
321 | hdmi_reg_init(hdmi_dev); | ||
322 | |||
323 | /* setting core registers */ | ||
324 | hdmi_timing_apply(hdmi_dev, conf); | ||
325 | |||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) | ||
330 | { | ||
331 | #define DUMPREG(reg_id) \ | ||
332 | dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \ | ||
333 | readl(hdev->regs + reg_id)) | ||
334 | |||
335 | dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix); | ||
336 | DUMPREG(HDMI_INTC_FLAG); | ||
337 | DUMPREG(HDMI_INTC_CON); | ||
338 | DUMPREG(HDMI_HPD_STATUS); | ||
339 | DUMPREG(HDMI_PHY_RSTOUT); | ||
340 | DUMPREG(HDMI_PHY_VPLL); | ||
341 | DUMPREG(HDMI_PHY_CMU); | ||
342 | DUMPREG(HDMI_CORE_RSTOUT); | ||
343 | |||
344 | dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix); | ||
345 | DUMPREG(HDMI_CON_0); | ||
346 | DUMPREG(HDMI_CON_1); | ||
347 | DUMPREG(HDMI_CON_2); | ||
348 | DUMPREG(HDMI_SYS_STATUS); | ||
349 | DUMPREG(HDMI_PHY_STATUS); | ||
350 | DUMPREG(HDMI_STATUS_EN); | ||
351 | DUMPREG(HDMI_HPD); | ||
352 | DUMPREG(HDMI_MODE_SEL); | ||
353 | DUMPREG(HDMI_HPD_GEN); | ||
354 | DUMPREG(HDMI_DC_CONTROL); | ||
355 | DUMPREG(HDMI_VIDEO_PATTERN_GEN); | ||
356 | |||
357 | dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix); | ||
358 | DUMPREG(HDMI_H_BLANK_0); | ||
359 | DUMPREG(HDMI_H_BLANK_1); | ||
360 | DUMPREG(HDMI_V_BLANK_0); | ||
361 | DUMPREG(HDMI_V_BLANK_1); | ||
362 | DUMPREG(HDMI_V_BLANK_2); | ||
363 | DUMPREG(HDMI_H_V_LINE_0); | ||
364 | DUMPREG(HDMI_H_V_LINE_1); | ||
365 | DUMPREG(HDMI_H_V_LINE_2); | ||
366 | DUMPREG(HDMI_VSYNC_POL); | ||
367 | DUMPREG(HDMI_INT_PRO_MODE); | ||
368 | DUMPREG(HDMI_V_BLANK_F_0); | ||
369 | DUMPREG(HDMI_V_BLANK_F_1); | ||
370 | DUMPREG(HDMI_V_BLANK_F_2); | ||
371 | DUMPREG(HDMI_H_SYNC_GEN_0); | ||
372 | DUMPREG(HDMI_H_SYNC_GEN_1); | ||
373 | DUMPREG(HDMI_H_SYNC_GEN_2); | ||
374 | DUMPREG(HDMI_V_SYNC_GEN_1_0); | ||
375 | DUMPREG(HDMI_V_SYNC_GEN_1_1); | ||
376 | DUMPREG(HDMI_V_SYNC_GEN_1_2); | ||
377 | DUMPREG(HDMI_V_SYNC_GEN_2_0); | ||
378 | DUMPREG(HDMI_V_SYNC_GEN_2_1); | ||
379 | DUMPREG(HDMI_V_SYNC_GEN_2_2); | ||
380 | DUMPREG(HDMI_V_SYNC_GEN_3_0); | ||
381 | DUMPREG(HDMI_V_SYNC_GEN_3_1); | ||
382 | DUMPREG(HDMI_V_SYNC_GEN_3_2); | ||
383 | |||
384 | dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix); | ||
385 | DUMPREG(HDMI_TG_CMD); | ||
386 | DUMPREG(HDMI_TG_H_FSZ_L); | ||
387 | DUMPREG(HDMI_TG_H_FSZ_H); | ||
388 | DUMPREG(HDMI_TG_HACT_ST_L); | ||
389 | DUMPREG(HDMI_TG_HACT_ST_H); | ||
390 | DUMPREG(HDMI_TG_HACT_SZ_L); | ||
391 | DUMPREG(HDMI_TG_HACT_SZ_H); | ||
392 | DUMPREG(HDMI_TG_V_FSZ_L); | ||
393 | DUMPREG(HDMI_TG_V_FSZ_H); | ||
394 | DUMPREG(HDMI_TG_VSYNC_L); | ||
395 | DUMPREG(HDMI_TG_VSYNC_H); | ||
396 | DUMPREG(HDMI_TG_VSYNC2_L); | ||
397 | DUMPREG(HDMI_TG_VSYNC2_H); | ||
398 | DUMPREG(HDMI_TG_VACT_ST_L); | ||
399 | DUMPREG(HDMI_TG_VACT_ST_H); | ||
400 | DUMPREG(HDMI_TG_VACT_SZ_L); | ||
401 | DUMPREG(HDMI_TG_VACT_SZ_H); | ||
402 | DUMPREG(HDMI_TG_FIELD_CHG_L); | ||
403 | DUMPREG(HDMI_TG_FIELD_CHG_H); | ||
404 | DUMPREG(HDMI_TG_VACT_ST2_L); | ||
405 | DUMPREG(HDMI_TG_VACT_ST2_H); | ||
406 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); | ||
407 | DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); | ||
408 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); | ||
409 | DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); | ||
410 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); | ||
411 | DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); | ||
412 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); | ||
413 | DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); | ||
414 | #undef DUMPREG | ||
415 | } | ||
416 | |||
417 | static const struct hdmi_preset_conf hdmi_conf_480p = { | ||
418 | .core = { | ||
419 | .h_blank = {0x8a, 0x00}, | ||
420 | .v_blank = {0x0d, 0x6a, 0x01}, | ||
421 | .h_v_line = {0x0d, 0xa2, 0x35}, | ||
422 | .vsync_pol = {0x01}, | ||
423 | .int_pro_mode = {0x00}, | ||
424 | .v_blank_f = {0x00, 0x00, 0x00}, | ||
425 | .h_sync_gen = {0x0e, 0x30, 0x11}, | ||
426 | .v_sync_gen1 = {0x0f, 0x90, 0x00}, | ||
427 | /* other don't care */ | ||
428 | }, | ||
429 | .tg = { | ||
430 | 0x00, /* cmd */ | ||
431 | 0x5a, 0x03, /* h_fsz */ | ||
432 | 0x8a, 0x00, 0xd0, 0x02, /* hact */ | ||
433 | 0x0d, 0x02, /* v_fsz */ | ||
434 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
435 | 0x2d, 0x00, 0xe0, 0x01, /* vact */ | ||
436 | 0x33, 0x02, /* field_chg */ | ||
437 | 0x49, 0x02, /* vact_st2 */ | ||
438 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
439 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
440 | }, | ||
441 | .mbus_fmt = { | ||
442 | .width = 720, | ||
443 | .height = 480, | ||
444 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
445 | .field = V4L2_FIELD_NONE, | ||
446 | }, | ||
447 | }; | ||
448 | |||
449 | static const struct hdmi_preset_conf hdmi_conf_720p60 = { | ||
450 | .core = { | ||
451 | .h_blank = {0x72, 0x01}, | ||
452 | .v_blank = {0xee, 0xf2, 0x00}, | ||
453 | .h_v_line = {0xee, 0x22, 0x67}, | ||
454 | .vsync_pol = {0x00}, | ||
455 | .int_pro_mode = {0x00}, | ||
456 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
457 | .h_sync_gen = {0x6c, 0x50, 0x02}, | ||
458 | .v_sync_gen1 = {0x0a, 0x50, 0x00}, | ||
459 | /* other don't care */ | ||
460 | }, | ||
461 | .tg = { | ||
462 | 0x00, /* cmd */ | ||
463 | 0x72, 0x06, /* h_fsz */ | ||
464 | 0x72, 0x01, 0x00, 0x05, /* hact */ | ||
465 | 0xee, 0x02, /* v_fsz */ | ||
466 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
467 | 0x1e, 0x00, 0xd0, 0x02, /* vact */ | ||
468 | 0x33, 0x02, /* field_chg */ | ||
469 | 0x49, 0x02, /* vact_st2 */ | ||
470 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
471 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
472 | }, | ||
473 | .mbus_fmt = { | ||
474 | .width = 1280, | ||
475 | .height = 720, | ||
476 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
477 | .field = V4L2_FIELD_NONE, | ||
478 | }, | ||
479 | }; | ||
480 | |||
481 | static const struct hdmi_preset_conf hdmi_conf_1080p50 = { | ||
482 | .core = { | ||
483 | .h_blank = {0xd0, 0x02}, | ||
484 | .v_blank = {0x65, 0x6c, 0x01}, | ||
485 | .h_v_line = {0x65, 0x04, 0xa5}, | ||
486 | .vsync_pol = {0x00}, | ||
487 | .int_pro_mode = {0x00}, | ||
488 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
489 | .h_sync_gen = {0x0e, 0xea, 0x08}, | ||
490 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
491 | /* other don't care */ | ||
492 | }, | ||
493 | .tg = { | ||
494 | 0x00, /* cmd */ | ||
495 | 0x98, 0x08, /* h_fsz */ | ||
496 | 0x18, 0x01, 0x80, 0x07, /* hact */ | ||
497 | 0x65, 0x04, /* v_fsz */ | ||
498 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
499 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
500 | 0x33, 0x02, /* field_chg */ | ||
501 | 0x49, 0x02, /* vact_st2 */ | ||
502 | 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ | ||
503 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
504 | }, | ||
505 | .mbus_fmt = { | ||
506 | .width = 1920, | ||
507 | .height = 1080, | ||
508 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
509 | .field = V4L2_FIELD_NONE, | ||
510 | }, | ||
511 | }; | ||
512 | |||
513 | static const struct hdmi_preset_conf hdmi_conf_1080p60 = { | ||
514 | .core = { | ||
515 | .h_blank = {0x18, 0x01}, | ||
516 | .v_blank = {0x65, 0x6c, 0x01}, | ||
517 | .h_v_line = {0x65, 0x84, 0x89}, | ||
518 | .vsync_pol = {0x00}, | ||
519 | .int_pro_mode = {0x00}, | ||
520 | .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ | ||
521 | .h_sync_gen = {0x56, 0x08, 0x02}, | ||
522 | .v_sync_gen1 = {0x09, 0x40, 0x00}, | ||
523 | /* other don't care */ | ||
524 | }, | ||
525 | .tg = { | ||
526 | 0x00, /* cmd */ | ||
527 | 0x98, 0x08, /* h_fsz */ | ||
528 | 0x18, 0x01, 0x80, 0x07, /* hact */ | ||
529 | 0x65, 0x04, /* v_fsz */ | ||
530 | 0x01, 0x00, 0x33, 0x02, /* vsync */ | ||
531 | 0x2d, 0x00, 0x38, 0x04, /* vact */ | ||
532 | 0x33, 0x02, /* field_chg */ | ||
533 | 0x48, 0x02, /* vact_st2 */ | ||
534 | 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ | ||
535 | 0x01, 0x00, 0x33, 0x02, /* field top/bot */ | ||
536 | }, | ||
537 | .mbus_fmt = { | ||
538 | .width = 1920, | ||
539 | .height = 1080, | ||
540 | .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ | ||
541 | .field = V4L2_FIELD_NONE, | ||
542 | }, | ||
543 | }; | ||
544 | |||
545 | static const struct { | ||
546 | u32 preset; | ||
547 | const struct hdmi_preset_conf *conf; | ||
548 | } hdmi_conf[] = { | ||
549 | { V4L2_DV_480P59_94, &hdmi_conf_480p }, | ||
550 | { V4L2_DV_720P59_94, &hdmi_conf_720p60 }, | ||
551 | { V4L2_DV_1080P50, &hdmi_conf_1080p50 }, | ||
552 | { V4L2_DV_1080P30, &hdmi_conf_1080p60 }, | ||
553 | { V4L2_DV_1080P60, &hdmi_conf_1080p60 }, | ||
554 | }; | ||
555 | |||
556 | static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset) | ||
557 | { | ||
558 | int i; | ||
559 | |||
560 | for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i) | ||
561 | if (hdmi_conf[i].preset == preset) | ||
562 | return hdmi_conf[i].conf; | ||
563 | return NULL; | ||
564 | } | ||
565 | |||
566 | static int hdmi_streamon(struct hdmi_device *hdev) | ||
567 | { | ||
568 | struct device *dev = hdev->dev; | ||
569 | struct hdmi_resources *res = &hdev->res; | ||
570 | int ret, tries; | ||
571 | |||
572 | dev_dbg(dev, "%s\n", __func__); | ||
573 | |||
574 | ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); | ||
575 | if (ret) | ||
576 | return ret; | ||
577 | |||
578 | /* waiting for HDMIPHY's PLL to get to steady state */ | ||
579 | for (tries = 100; tries; --tries) { | ||
580 | u32 val = hdmi_read(hdev, HDMI_PHY_STATUS); | ||
581 | if (val & HDMI_PHY_STATUS_READY) | ||
582 | break; | ||
583 | mdelay(1); | ||
584 | } | ||
585 | /* steady state not achieved */ | ||
586 | if (tries == 0) { | ||
587 | dev_err(dev, "hdmiphy's pll could not reach steady state.\n"); | ||
588 | v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); | ||
589 | hdmi_dumpregs(hdev, "s_stream"); | ||
590 | return -EIO; | ||
591 | } | ||
592 | |||
593 | /* hdmiphy clock is used for HDMI in streaming mode */ | ||
594 | clk_disable(res->sclk_hdmi); | ||
595 | clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy); | ||
596 | clk_enable(res->sclk_hdmi); | ||
597 | |||
598 | /* enable HDMI and timing generator */ | ||
599 | hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN); | ||
600 | hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN); | ||
601 | hdmi_dumpregs(hdev, "streamon"); | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int hdmi_streamoff(struct hdmi_device *hdev) | ||
606 | { | ||
607 | struct device *dev = hdev->dev; | ||
608 | struct hdmi_resources *res = &hdev->res; | ||
609 | |||
610 | dev_dbg(dev, "%s\n", __func__); | ||
611 | |||
612 | hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN); | ||
613 | hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN); | ||
614 | |||
615 | /* pixel(vpll) clock is used for HDMI in config mode */ | ||
616 | clk_disable(res->sclk_hdmi); | ||
617 | clk_set_parent(res->sclk_hdmi, res->sclk_pixel); | ||
618 | clk_enable(res->sclk_hdmi); | ||
619 | |||
620 | v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0); | ||
621 | |||
622 | hdmi_dumpregs(hdev, "streamoff"); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) | ||
627 | { | ||
628 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
629 | struct device *dev = hdev->dev; | ||
630 | |||
631 | dev_dbg(dev, "%s(%d)\n", __func__, enable); | ||
632 | if (enable) | ||
633 | return hdmi_streamon(hdev); | ||
634 | return hdmi_streamoff(hdev); | ||
635 | } | ||
636 | |||
637 | static void hdmi_resource_poweron(struct hdmi_resources *res) | ||
638 | { | ||
639 | /* turn HDMI power on */ | ||
640 | regulator_bulk_enable(res->regul_count, res->regul_bulk); | ||
641 | /* power-on hdmi physical interface */ | ||
642 | clk_enable(res->hdmiphy); | ||
643 | /* use VPP as parent clock; HDMIPHY is not working yet */ | ||
644 | clk_set_parent(res->sclk_hdmi, res->sclk_pixel); | ||
645 | /* turn clocks on */ | ||
646 | clk_enable(res->sclk_hdmi); | ||
647 | } | ||
648 | |||
649 | static void hdmi_resource_poweroff(struct hdmi_resources *res) | ||
650 | { | ||
651 | /* turn clocks off */ | ||
652 | clk_disable(res->sclk_hdmi); | ||
653 | /* power-off hdmiphy */ | ||
654 | clk_disable(res->hdmiphy); | ||
655 | /* turn HDMI power off */ | ||
656 | regulator_bulk_disable(res->regul_count, res->regul_bulk); | ||
657 | } | ||
658 | |||
659 | static int hdmi_s_power(struct v4l2_subdev *sd, int on) | ||
660 | { | ||
661 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
662 | int ret; | ||
663 | |||
664 | if (on) | ||
665 | ret = pm_runtime_get_sync(hdev->dev); | ||
666 | else | ||
667 | ret = pm_runtime_put_sync(hdev->dev); | ||
668 | /* only values < 0 indicate errors */ | ||
669 | return IS_ERR_VALUE(ret) ? ret : 0; | ||
670 | } | ||
671 | |||
672 | static int hdmi_s_dv_preset(struct v4l2_subdev *sd, | ||
673 | struct v4l2_dv_preset *preset) | ||
674 | { | ||
675 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
676 | struct device *dev = hdev->dev; | ||
677 | const struct hdmi_preset_conf *conf; | ||
678 | |||
679 | conf = hdmi_preset2conf(preset->preset); | ||
680 | if (conf == NULL) { | ||
681 | dev_err(dev, "preset (%u) not supported\n", preset->preset); | ||
682 | return -EINVAL; | ||
683 | } | ||
684 | hdev->cur_conf = conf; | ||
685 | hdev->cur_preset = preset->preset; | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int hdmi_g_dv_preset(struct v4l2_subdev *sd, | ||
690 | struct v4l2_dv_preset *preset) | ||
691 | { | ||
692 | memset(preset, 0, sizeof(*preset)); | ||
693 | preset->preset = sd_to_hdmi_dev(sd)->cur_preset; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, | ||
698 | struct v4l2_mbus_framefmt *fmt) | ||
699 | { | ||
700 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
701 | struct device *dev = hdev->dev; | ||
702 | |||
703 | dev_dbg(dev, "%s\n", __func__); | ||
704 | if (!hdev->cur_conf) | ||
705 | return -EINVAL; | ||
706 | *fmt = hdev->cur_conf->mbus_fmt; | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, | ||
711 | struct v4l2_dv_enum_preset *preset) | ||
712 | { | ||
713 | if (preset->index >= ARRAY_SIZE(hdmi_conf)) | ||
714 | return -EINVAL; | ||
715 | return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset); | ||
716 | } | ||
717 | |||
718 | static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { | ||
719 | .s_power = hdmi_s_power, | ||
720 | }; | ||
721 | |||
722 | static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = { | ||
723 | .s_dv_preset = hdmi_s_dv_preset, | ||
724 | .g_dv_preset = hdmi_g_dv_preset, | ||
725 | .enum_dv_presets = hdmi_enum_dv_presets, | ||
726 | .g_mbus_fmt = hdmi_g_mbus_fmt, | ||
727 | .s_stream = hdmi_s_stream, | ||
728 | }; | ||
729 | |||
730 | static const struct v4l2_subdev_ops hdmi_sd_ops = { | ||
731 | .core = &hdmi_sd_core_ops, | ||
732 | .video = &hdmi_sd_video_ops, | ||
733 | }; | ||
734 | |||
735 | static int hdmi_runtime_suspend(struct device *dev) | ||
736 | { | ||
737 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
738 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
739 | |||
740 | dev_dbg(dev, "%s\n", __func__); | ||
741 | hdmi_resource_poweroff(&hdev->res); | ||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int hdmi_runtime_resume(struct device *dev) | ||
746 | { | ||
747 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
748 | struct hdmi_device *hdev = sd_to_hdmi_dev(sd); | ||
749 | int ret = 0; | ||
750 | |||
751 | dev_dbg(dev, "%s\n", __func__); | ||
752 | |||
753 | hdmi_resource_poweron(&hdev->res); | ||
754 | |||
755 | ret = hdmi_conf_apply(hdev); | ||
756 | if (ret) | ||
757 | goto fail; | ||
758 | |||
759 | dev_dbg(dev, "poweron succeed\n"); | ||
760 | |||
761 | return 0; | ||
762 | |||
763 | fail: | ||
764 | hdmi_resource_poweroff(&hdev->res); | ||
765 | dev_err(dev, "poweron failed\n"); | ||
766 | |||
767 | return ret; | ||
768 | } | ||
769 | |||
770 | static const struct dev_pm_ops hdmi_pm_ops = { | ||
771 | .runtime_suspend = hdmi_runtime_suspend, | ||
772 | .runtime_resume = hdmi_runtime_resume, | ||
773 | }; | ||
774 | |||
775 | static void hdmi_resources_cleanup(struct hdmi_device *hdev) | ||
776 | { | ||
777 | struct hdmi_resources *res = &hdev->res; | ||
778 | |||
779 | dev_dbg(hdev->dev, "HDMI resource cleanup\n"); | ||
780 | /* put clocks, power */ | ||
781 | if (res->regul_count) | ||
782 | regulator_bulk_free(res->regul_count, res->regul_bulk); | ||
783 | /* kfree is NULL-safe */ | ||
784 | kfree(res->regul_bulk); | ||
785 | if (!IS_ERR_OR_NULL(res->hdmiphy)) | ||
786 | clk_put(res->hdmiphy); | ||
787 | if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) | ||
788 | clk_put(res->sclk_hdmiphy); | ||
789 | if (!IS_ERR_OR_NULL(res->sclk_pixel)) | ||
790 | clk_put(res->sclk_pixel); | ||
791 | if (!IS_ERR_OR_NULL(res->sclk_hdmi)) | ||
792 | clk_put(res->sclk_hdmi); | ||
793 | if (!IS_ERR_OR_NULL(res->hdmi)) | ||
794 | clk_put(res->hdmi); | ||
795 | memset(res, 0, sizeof *res); | ||
796 | } | ||
797 | |||
798 | static int hdmi_resources_init(struct hdmi_device *hdev) | ||
799 | { | ||
800 | struct device *dev = hdev->dev; | ||
801 | struct hdmi_resources *res = &hdev->res; | ||
802 | static char *supply[] = { | ||
803 | "hdmi-en", | ||
804 | "vdd", | ||
805 | "vdd_osc", | ||
806 | "vdd_pll", | ||
807 | }; | ||
808 | int i, ret; | ||
809 | |||
810 | dev_dbg(dev, "HDMI resource init\n"); | ||
811 | |||
812 | memset(res, 0, sizeof *res); | ||
813 | /* get clocks, power */ | ||
814 | |||
815 | res->hdmi = clk_get(dev, "hdmi"); | ||
816 | if (IS_ERR_OR_NULL(res->hdmi)) { | ||
817 | dev_err(dev, "failed to get clock 'hdmi'\n"); | ||
818 | goto fail; | ||
819 | } | ||
820 | res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); | ||
821 | if (IS_ERR_OR_NULL(res->sclk_hdmi)) { | ||
822 | dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); | ||
823 | goto fail; | ||
824 | } | ||
825 | res->sclk_pixel = clk_get(dev, "sclk_pixel"); | ||
826 | if (IS_ERR_OR_NULL(res->sclk_pixel)) { | ||
827 | dev_err(dev, "failed to get clock 'sclk_pixel'\n"); | ||
828 | goto fail; | ||
829 | } | ||
830 | res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); | ||
831 | if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { | ||
832 | dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); | ||
833 | goto fail; | ||
834 | } | ||
835 | res->hdmiphy = clk_get(dev, "hdmiphy"); | ||
836 | if (IS_ERR_OR_NULL(res->hdmiphy)) { | ||
837 | dev_err(dev, "failed to get clock 'hdmiphy'\n"); | ||
838 | goto fail; | ||
839 | } | ||
840 | res->regul_bulk = kzalloc(ARRAY_SIZE(supply) * | ||
841 | sizeof res->regul_bulk[0], GFP_KERNEL); | ||
842 | if (!res->regul_bulk) { | ||
843 | dev_err(dev, "failed to get memory for regulators\n"); | ||
844 | goto fail; | ||
845 | } | ||
846 | for (i = 0; i < ARRAY_SIZE(supply); ++i) { | ||
847 | res->regul_bulk[i].supply = supply[i]; | ||
848 | res->regul_bulk[i].consumer = NULL; | ||
849 | } | ||
850 | |||
851 | ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk); | ||
852 | if (ret) { | ||
853 | dev_err(dev, "failed to get regulators\n"); | ||
854 | goto fail; | ||
855 | } | ||
856 | res->regul_count = ARRAY_SIZE(supply); | ||
857 | |||
858 | return 0; | ||
859 | fail: | ||
860 | dev_err(dev, "HDMI resource init - failed\n"); | ||
861 | hdmi_resources_cleanup(hdev); | ||
862 | return -ENODEV; | ||
863 | } | ||
864 | |||
865 | static int __devinit hdmi_probe(struct platform_device *pdev) | ||
866 | { | ||
867 | struct device *dev = &pdev->dev; | ||
868 | struct resource *res; | ||
869 | struct i2c_adapter *phy_adapter; | ||
870 | struct v4l2_subdev *sd; | ||
871 | struct hdmi_device *hdmi_dev = NULL; | ||
872 | struct hdmi_driver_data *drv_data; | ||
873 | int ret; | ||
874 | |||
875 | dev_dbg(dev, "probe start\n"); | ||
876 | |||
877 | hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL); | ||
878 | if (!hdmi_dev) { | ||
879 | dev_err(dev, "out of memory\n"); | ||
880 | ret = -ENOMEM; | ||
881 | goto fail; | ||
882 | } | ||
883 | |||
884 | hdmi_dev->dev = dev; | ||
885 | |||
886 | ret = hdmi_resources_init(hdmi_dev); | ||
887 | if (ret) | ||
888 | goto fail_hdev; | ||
889 | |||
890 | /* mapping HDMI registers */ | ||
891 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
892 | if (res == NULL) { | ||
893 | dev_err(dev, "get memory resource failed.\n"); | ||
894 | ret = -ENXIO; | ||
895 | goto fail_init; | ||
896 | } | ||
897 | |||
898 | hdmi_dev->regs = ioremap(res->start, resource_size(res)); | ||
899 | if (hdmi_dev->regs == NULL) { | ||
900 | dev_err(dev, "register mapping failed.\n"); | ||
901 | ret = -ENXIO; | ||
902 | goto fail_hdev; | ||
903 | } | ||
904 | |||
905 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
906 | if (res == NULL) { | ||
907 | dev_err(dev, "get interrupt resource failed.\n"); | ||
908 | ret = -ENXIO; | ||
909 | goto fail_regs; | ||
910 | } | ||
911 | |||
912 | ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev); | ||
913 | if (ret) { | ||
914 | dev_err(dev, "request interrupt failed.\n"); | ||
915 | goto fail_regs; | ||
916 | } | ||
917 | hdmi_dev->irq = res->start; | ||
918 | |||
919 | /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */ | ||
920 | strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev), | ||
921 | sizeof(hdmi_dev->v4l2_dev.name)); | ||
922 | /* passing NULL owner prevents driver from erasing drvdata */ | ||
923 | ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev); | ||
924 | if (ret) { | ||
925 | dev_err(dev, "could not register v4l2 device.\n"); | ||
926 | goto fail_irq; | ||
927 | } | ||
928 | |||
929 | drv_data = (struct hdmi_driver_data *) | ||
930 | platform_get_device_id(pdev)->driver_data; | ||
931 | phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus); | ||
932 | if (phy_adapter == NULL) { | ||
933 | dev_err(dev, "adapter request failed\n"); | ||
934 | ret = -ENXIO; | ||
935 | goto fail_vdev; | ||
936 | } | ||
937 | |||
938 | hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev, | ||
939 | phy_adapter, &hdmiphy_info, NULL); | ||
940 | /* on failure or not adapter is no longer useful */ | ||
941 | i2c_put_adapter(phy_adapter); | ||
942 | if (hdmi_dev->phy_sd == NULL) { | ||
943 | dev_err(dev, "missing subdev for hdmiphy\n"); | ||
944 | ret = -ENODEV; | ||
945 | goto fail_vdev; | ||
946 | } | ||
947 | |||
948 | clk_enable(hdmi_dev->res.hdmi); | ||
949 | |||
950 | pm_runtime_enable(dev); | ||
951 | |||
952 | sd = &hdmi_dev->sd; | ||
953 | v4l2_subdev_init(sd, &hdmi_sd_ops); | ||
954 | sd->owner = THIS_MODULE; | ||
955 | |||
956 | strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); | ||
957 | hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; | ||
958 | /* FIXME: missing fail preset is not supported */ | ||
959 | hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset); | ||
960 | |||
961 | /* storing subdev for call that have only access to struct device */ | ||
962 | dev_set_drvdata(dev, sd); | ||
963 | |||
964 | dev_info(dev, "probe sucessful\n"); | ||
965 | |||
966 | return 0; | ||
967 | |||
968 | fail_vdev: | ||
969 | v4l2_device_unregister(&hdmi_dev->v4l2_dev); | ||
970 | |||
971 | fail_irq: | ||
972 | free_irq(hdmi_dev->irq, hdmi_dev); | ||
973 | |||
974 | fail_regs: | ||
975 | iounmap(hdmi_dev->regs); | ||
976 | |||
977 | fail_init: | ||
978 | hdmi_resources_cleanup(hdmi_dev); | ||
979 | |||
980 | fail_hdev: | ||
981 | kfree(hdmi_dev); | ||
982 | |||
983 | fail: | ||
984 | dev_err(dev, "probe failed\n"); | ||
985 | return ret; | ||
986 | } | ||
987 | |||
988 | static int __devexit hdmi_remove(struct platform_device *pdev) | ||
989 | { | ||
990 | struct device *dev = &pdev->dev; | ||
991 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
992 | struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd); | ||
993 | |||
994 | pm_runtime_disable(dev); | ||
995 | clk_disable(hdmi_dev->res.hdmi); | ||
996 | v4l2_device_unregister(&hdmi_dev->v4l2_dev); | ||
997 | disable_irq(hdmi_dev->irq); | ||
998 | free_irq(hdmi_dev->irq, hdmi_dev); | ||
999 | iounmap(hdmi_dev->regs); | ||
1000 | hdmi_resources_cleanup(hdmi_dev); | ||
1001 | kfree(hdmi_dev); | ||
1002 | dev_info(dev, "remove sucessful\n"); | ||
1003 | |||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | static struct platform_driver hdmi_driver __refdata = { | ||
1008 | .probe = hdmi_probe, | ||
1009 | .remove = __devexit_p(hdmi_remove), | ||
1010 | .id_table = hdmi_driver_types, | ||
1011 | .driver = { | ||
1012 | .name = "s5p-hdmi", | ||
1013 | .owner = THIS_MODULE, | ||
1014 | .pm = &hdmi_pm_ops, | ||
1015 | } | ||
1016 | }; | ||
1017 | |||
1018 | /* D R I V E R I N I T I A L I Z A T I O N */ | ||
1019 | |||
1020 | static int __init hdmi_init(void) | ||
1021 | { | ||
1022 | int ret; | ||
1023 | static const char banner[] __initdata = KERN_INFO \ | ||
1024 | "Samsung HDMI output driver, " | ||
1025 | "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; | ||
1026 | printk(banner); | ||
1027 | |||
1028 | ret = platform_driver_register(&hdmi_driver); | ||
1029 | if (ret) | ||
1030 | printk(KERN_ERR "HDMI platform driver register failed\n"); | ||
1031 | |||
1032 | return ret; | ||
1033 | } | ||
1034 | module_init(hdmi_init); | ||
1035 | |||
1036 | static void __exit hdmi_exit(void) | ||
1037 | { | ||
1038 | platform_driver_unregister(&hdmi_driver); | ||
1039 | } | ||
1040 | module_exit(hdmi_exit); | ||
1041 | |||
1042 | |||
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c new file mode 100644 index 00000000000..6693f4aff10 --- /dev/null +++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * Samsung HDMI Physical interface driver | ||
3 | * | ||
4 | * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd | ||
5 | * Author: Tomasz Stanislawski <t.stanislaws@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 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/clk.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/err.h> | ||
21 | |||
22 | #include <media/v4l2-subdev.h> | ||
23 | |||
24 | MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>"); | ||
25 | MODULE_DESCRIPTION("Samsung HDMI Physical interface driver"); | ||
26 | MODULE_LICENSE("GPL"); | ||
27 | |||
28 | struct hdmiphy_conf { | ||
29 | u32 preset; | ||
30 | const u8 *data; | ||
31 | }; | ||
32 | |||
33 | static const u8 hdmiphy_conf27[32] = { | ||
34 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, | ||
35 | 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87, | ||
36 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
37 | 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00, | ||
38 | }; | ||
39 | |||
40 | static const u8 hdmiphy_conf74_175[32] = { | ||
41 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, | ||
42 | 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, | ||
43 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
44 | 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, | ||
45 | }; | ||
46 | |||
47 | static const u8 hdmiphy_conf74_25[32] = { | ||
48 | 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, | ||
49 | 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, | ||
50 | 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0, | ||
51 | 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, | ||
52 | }; | ||
53 | |||
54 | static const u8 hdmiphy_conf148_5[32] = { | ||
55 | 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, | ||
56 | 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, | ||
57 | 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, | ||
58 | 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, | ||
59 | }; | ||
60 | |||
61 | static const struct hdmiphy_conf hdmiphy_conf[] = { | ||
62 | { V4L2_DV_480P59_94, hdmiphy_conf27 }, | ||
63 | { V4L2_DV_1080P30, hdmiphy_conf74_175 }, | ||
64 | { V4L2_DV_720P59_94, hdmiphy_conf74_175 }, | ||
65 | { V4L2_DV_720P60, hdmiphy_conf74_25 }, | ||
66 | { V4L2_DV_1080P50, hdmiphy_conf148_5 }, | ||
67 | { V4L2_DV_1080P60, hdmiphy_conf148_5 }, | ||
68 | }; | ||
69 | |||
70 | const u8 *hdmiphy_preset2conf(u32 preset) | ||
71 | { | ||
72 | int i; | ||
73 | for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i) | ||
74 | if (hdmiphy_conf[i].preset == preset) | ||
75 | return hdmiphy_conf[i].data; | ||
76 | return NULL; | ||
77 | } | ||
78 | |||
79 | static int hdmiphy_s_power(struct v4l2_subdev *sd, int on) | ||
80 | { | ||
81 | /* to be implemented */ | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, | ||
86 | struct v4l2_dv_preset *preset) | ||
87 | { | ||
88 | const u8 *data; | ||
89 | u8 buffer[32]; | ||
90 | int ret; | ||
91 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
92 | struct device *dev = &client->dev; | ||
93 | |||
94 | dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset); | ||
95 | data = hdmiphy_preset2conf(preset->preset); | ||
96 | if (!data) { | ||
97 | dev_err(dev, "format not supported\n"); | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | |||
101 | /* storing configuration to the device */ | ||
102 | memcpy(buffer, data, 32); | ||
103 | ret = i2c_master_send(client, buffer, 32); | ||
104 | if (ret != 32) { | ||
105 | dev_err(dev, "failed to configure HDMIPHY via I2C\n"); | ||
106 | return -EIO; | ||
107 | } | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable) | ||
113 | { | ||
114 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
115 | struct device *dev = &client->dev; | ||
116 | u8 buffer[2]; | ||
117 | int ret; | ||
118 | |||
119 | dev_info(dev, "s_stream(%d)\n", enable); | ||
120 | /* going to/from configuration from/to operation mode */ | ||
121 | buffer[0] = 0x1f; | ||
122 | buffer[1] = enable ? 0x80 : 0x00; | ||
123 | |||
124 | ret = i2c_master_send(client, buffer, 2); | ||
125 | if (ret != 2) { | ||
126 | dev_err(dev, "stream (%d) failed\n", enable); | ||
127 | return -EIO; | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static const struct v4l2_subdev_core_ops hdmiphy_core_ops = { | ||
133 | .s_power = hdmiphy_s_power, | ||
134 | }; | ||
135 | |||
136 | static const struct v4l2_subdev_video_ops hdmiphy_video_ops = { | ||
137 | .s_dv_preset = hdmiphy_s_dv_preset, | ||
138 | .s_stream = hdmiphy_s_stream, | ||
139 | }; | ||
140 | |||
141 | static const struct v4l2_subdev_ops hdmiphy_ops = { | ||
142 | .core = &hdmiphy_core_ops, | ||
143 | .video = &hdmiphy_video_ops, | ||
144 | }; | ||
145 | |||
146 | static int __devinit hdmiphy_probe(struct i2c_client *client, | ||
147 | const struct i2c_device_id *id) | ||
148 | { | ||
149 | static struct v4l2_subdev sd; | ||
150 | |||
151 | v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops); | ||
152 | dev_info(&client->dev, "probe successful\n"); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static int __devexit hdmiphy_remove(struct i2c_client *client) | ||
157 | { | ||
158 | dev_info(&client->dev, "remove successful\n"); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static const struct i2c_device_id hdmiphy_id[] = { | ||
163 | { "hdmiphy", 0 }, | ||
164 | { }, | ||
165 | }; | ||
166 | MODULE_DEVICE_TABLE(i2c, hdmiphy_id); | ||
167 | |||
168 | static struct i2c_driver hdmiphy_driver = { | ||
169 | .driver = { | ||
170 | .name = "s5p-hdmiphy", | ||
171 | .owner = THIS_MODULE, | ||
172 | }, | ||
173 | .probe = hdmiphy_probe, | ||
174 | .remove = __devexit_p(hdmiphy_remove), | ||
175 | .id_table = hdmiphy_id, | ||
176 | }; | ||
177 | |||
178 | static int __init hdmiphy_init(void) | ||
179 | { | ||
180 | return i2c_add_driver(&hdmiphy_driver); | ||
181 | } | ||
182 | module_init(hdmiphy_init); | ||
183 | |||
184 | static void __exit hdmiphy_exit(void) | ||
185 | { | ||
186 | i2c_del_driver(&hdmiphy_driver); | ||
187 | } | ||
188 | module_exit(hdmiphy_exit); | ||
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h new file mode 100644 index 00000000000..e2242243f63 --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer.h | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #ifndef SAMSUNG_MIXER_H | ||
15 | #define SAMSUNG_MIXER_H | ||
16 | |||
17 | #ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG | ||
18 | #define DEBUG | ||
19 | #endif | ||
20 | |||
21 | #include <linux/fb.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <media/v4l2-device.h> | ||
26 | #include <media/videobuf2-core.h> | ||
27 | |||
28 | #include "regs-mixer.h" | ||
29 | |||
30 | /** maximum number of output interfaces */ | ||
31 | #define MXR_MAX_OUTPUTS 2 | ||
32 | /** maximum number of input interfaces (layers) */ | ||
33 | #define MXR_MAX_LAYERS 3 | ||
34 | #define MXR_DRIVER_NAME "s5p-mixer" | ||
35 | /** maximal number of planes for every layer */ | ||
36 | #define MXR_MAX_PLANES 2 | ||
37 | |||
38 | #define MXR_ENABLE 1 | ||
39 | #define MXR_DISABLE 0 | ||
40 | |||
41 | /** description of a macroblock for packed formats */ | ||
42 | struct mxr_block { | ||
43 | /** vertical number of pixels in macroblock */ | ||
44 | unsigned int width; | ||
45 | /** horizontal number of pixels in macroblock */ | ||
46 | unsigned int height; | ||
47 | /** size of block in bytes */ | ||
48 | unsigned int size; | ||
49 | }; | ||
50 | |||
51 | /** description of supported format */ | ||
52 | struct mxr_format { | ||
53 | /** format name/mnemonic */ | ||
54 | const char *name; | ||
55 | /** fourcc identifier */ | ||
56 | u32 fourcc; | ||
57 | /** colorspace identifier */ | ||
58 | enum v4l2_colorspace colorspace; | ||
59 | /** number of planes in image data */ | ||
60 | int num_planes; | ||
61 | /** description of block for each plane */ | ||
62 | struct mxr_block plane[MXR_MAX_PLANES]; | ||
63 | /** number of subframes in image data */ | ||
64 | int num_subframes; | ||
65 | /** specifies to which subframe belong given plane */ | ||
66 | int plane2subframe[MXR_MAX_PLANES]; | ||
67 | /** internal code, driver dependant */ | ||
68 | unsigned long cookie; | ||
69 | }; | ||
70 | |||
71 | /** description of crop configuration for image */ | ||
72 | struct mxr_crop { | ||
73 | /** width of layer in pixels */ | ||
74 | unsigned int full_width; | ||
75 | /** height of layer in pixels */ | ||
76 | unsigned int full_height; | ||
77 | /** horizontal offset of first pixel to be displayed */ | ||
78 | unsigned int x_offset; | ||
79 | /** vertical offset of first pixel to be displayed */ | ||
80 | unsigned int y_offset; | ||
81 | /** width of displayed data in pixels */ | ||
82 | unsigned int width; | ||
83 | /** height of displayed data in pixels */ | ||
84 | unsigned int height; | ||
85 | /** indicate which fields are present in buffer */ | ||
86 | unsigned int field; | ||
87 | }; | ||
88 | |||
89 | /** description of transformation from source to destination image */ | ||
90 | struct mxr_geometry { | ||
91 | /** cropping for source image */ | ||
92 | struct mxr_crop src; | ||
93 | /** cropping for destination image */ | ||
94 | struct mxr_crop dst; | ||
95 | /** layer-dependant description of horizontal scaling */ | ||
96 | unsigned int x_ratio; | ||
97 | /** layer-dependant description of vertical scaling */ | ||
98 | unsigned int y_ratio; | ||
99 | }; | ||
100 | |||
101 | /** instance of a buffer */ | ||
102 | struct mxr_buffer { | ||
103 | /** common v4l buffer stuff -- must be first */ | ||
104 | struct vb2_buffer vb; | ||
105 | /** node for layer's lists */ | ||
106 | struct list_head list; | ||
107 | }; | ||
108 | |||
109 | |||
110 | /** internal states of layer */ | ||
111 | enum mxr_layer_state { | ||
112 | /** layers is not shown */ | ||
113 | MXR_LAYER_IDLE = 0, | ||
114 | /** state between STREAMON and hardware start */ | ||
115 | MXR_LAYER_STREAMING_START, | ||
116 | /** layer is shown */ | ||
117 | MXR_LAYER_STREAMING, | ||
118 | /** state before STREAMOFF is finished */ | ||
119 | MXR_LAYER_STREAMING_FINISH, | ||
120 | }; | ||
121 | |||
122 | /** forward declarations */ | ||
123 | struct mxr_device; | ||
124 | struct mxr_layer; | ||
125 | |||
126 | /** callback for layers operation */ | ||
127 | struct mxr_layer_ops { | ||
128 | /* TODO: try to port it to subdev API */ | ||
129 | /** handler for resource release function */ | ||
130 | void (*release)(struct mxr_layer *); | ||
131 | /** setting buffer to HW */ | ||
132 | void (*buffer_set)(struct mxr_layer *, struct mxr_buffer *); | ||
133 | /** setting format and geometry in HW */ | ||
134 | void (*format_set)(struct mxr_layer *); | ||
135 | /** streaming stop/start */ | ||
136 | void (*stream_set)(struct mxr_layer *, int); | ||
137 | /** adjusting geometry */ | ||
138 | void (*fix_geometry)(struct mxr_layer *); | ||
139 | }; | ||
140 | |||
141 | /** layer instance, a single window and content displayed on output */ | ||
142 | struct mxr_layer { | ||
143 | /** parent mixer device */ | ||
144 | struct mxr_device *mdev; | ||
145 | /** layer index (unique identifier) */ | ||
146 | int idx; | ||
147 | /** callbacks for layer methods */ | ||
148 | struct mxr_layer_ops ops; | ||
149 | /** format array */ | ||
150 | const struct mxr_format **fmt_array; | ||
151 | /** size of format array */ | ||
152 | unsigned long fmt_array_size; | ||
153 | |||
154 | /** lock for protection of list and state fields */ | ||
155 | spinlock_t enq_slock; | ||
156 | /** list for enqueued buffers */ | ||
157 | struct list_head enq_list; | ||
158 | /** buffer currently owned by hardware in temporary registers */ | ||
159 | struct mxr_buffer *update_buf; | ||
160 | /** buffer currently owned by hardware in shadow registers */ | ||
161 | struct mxr_buffer *shadow_buf; | ||
162 | /** state of layer IDLE/STREAMING */ | ||
163 | enum mxr_layer_state state; | ||
164 | |||
165 | /** mutex for protection of fields below */ | ||
166 | struct mutex mutex; | ||
167 | /** handler for video node */ | ||
168 | struct video_device vfd; | ||
169 | /** queue for output buffers */ | ||
170 | struct vb2_queue vb_queue; | ||
171 | /** current image format */ | ||
172 | const struct mxr_format *fmt; | ||
173 | /** current geometry of image */ | ||
174 | struct mxr_geometry geo; | ||
175 | }; | ||
176 | |||
177 | /** description of mixers output interface */ | ||
178 | struct mxr_output { | ||
179 | /** name of output */ | ||
180 | char name[32]; | ||
181 | /** output subdev */ | ||
182 | struct v4l2_subdev *sd; | ||
183 | /** cookie used for configuration of registers */ | ||
184 | int cookie; | ||
185 | }; | ||
186 | |||
187 | /** specify source of output subdevs */ | ||
188 | struct mxr_output_conf { | ||
189 | /** name of output (connector) */ | ||
190 | char *output_name; | ||
191 | /** name of module that generates output subdev */ | ||
192 | char *module_name; | ||
193 | /** cookie need for mixer HW */ | ||
194 | int cookie; | ||
195 | }; | ||
196 | |||
197 | struct clk; | ||
198 | struct regulator; | ||
199 | |||
200 | /** auxiliary resources used my mixer */ | ||
201 | struct mxr_resources { | ||
202 | /** interrupt index */ | ||
203 | int irq; | ||
204 | /** pointer to Mixer registers */ | ||
205 | void __iomem *mxr_regs; | ||
206 | /** pointer to Video Processor registers */ | ||
207 | void __iomem *vp_regs; | ||
208 | /** other resources, should used under mxr_device.mutex */ | ||
209 | struct clk *mixer; | ||
210 | struct clk *vp; | ||
211 | struct clk *sclk_mixer; | ||
212 | struct clk *sclk_hdmi; | ||
213 | struct clk *sclk_dac; | ||
214 | }; | ||
215 | |||
216 | /* event flags used */ | ||
217 | enum mxr_devide_flags { | ||
218 | MXR_EVENT_VSYNC = 0, | ||
219 | }; | ||
220 | |||
221 | /** drivers instance */ | ||
222 | struct mxr_device { | ||
223 | /** master device */ | ||
224 | struct device *dev; | ||
225 | /** state of each layer */ | ||
226 | struct mxr_layer *layer[MXR_MAX_LAYERS]; | ||
227 | /** state of each output */ | ||
228 | struct mxr_output *output[MXR_MAX_OUTPUTS]; | ||
229 | /** number of registered outputs */ | ||
230 | int output_cnt; | ||
231 | |||
232 | /* video resources */ | ||
233 | |||
234 | /** V4L2 device */ | ||
235 | struct v4l2_device v4l2_dev; | ||
236 | /** context of allocator */ | ||
237 | void *alloc_ctx; | ||
238 | /** event wait queue */ | ||
239 | wait_queue_head_t event_queue; | ||
240 | /** state flags */ | ||
241 | unsigned long event_flags; | ||
242 | |||
243 | /** spinlock for protection of registers */ | ||
244 | spinlock_t reg_slock; | ||
245 | |||
246 | /** mutex for protection of fields below */ | ||
247 | struct mutex mutex; | ||
248 | /** number of entities depndant on output configuration */ | ||
249 | int n_output; | ||
250 | /** number of users that do streaming */ | ||
251 | int n_streamer; | ||
252 | /** index of current output */ | ||
253 | int current_output; | ||
254 | /** auxiliary resources used my mixer */ | ||
255 | struct mxr_resources res; | ||
256 | }; | ||
257 | |||
258 | /** transform device structure into mixer device */ | ||
259 | static inline struct mxr_device *to_mdev(struct device *dev) | ||
260 | { | ||
261 | struct v4l2_device *vdev = dev_get_drvdata(dev); | ||
262 | return container_of(vdev, struct mxr_device, v4l2_dev); | ||
263 | } | ||
264 | |||
265 | /** get current output data, should be called under mdev's mutex */ | ||
266 | static inline struct mxr_output *to_output(struct mxr_device *mdev) | ||
267 | { | ||
268 | return mdev->output[mdev->current_output]; | ||
269 | } | ||
270 | |||
271 | /** get current output subdev, should be called under mdev's mutex */ | ||
272 | static inline struct v4l2_subdev *to_outsd(struct mxr_device *mdev) | ||
273 | { | ||
274 | struct mxr_output *out = to_output(mdev); | ||
275 | return out ? out->sd : NULL; | ||
276 | } | ||
277 | |||
278 | /** forward declaration for mixer platform data */ | ||
279 | struct mxr_platform_data; | ||
280 | |||
281 | /** acquiring common video resources */ | ||
282 | int __devinit mxr_acquire_video(struct mxr_device *mdev, | ||
283 | struct mxr_output_conf *output_cont, int output_count); | ||
284 | |||
285 | /** releasing common video resources */ | ||
286 | void __devexit mxr_release_video(struct mxr_device *mdev); | ||
287 | |||
288 | struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); | ||
289 | struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); | ||
290 | struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, | ||
291 | int idx, char *name, struct mxr_layer_ops *ops); | ||
292 | |||
293 | void mxr_base_layer_release(struct mxr_layer *layer); | ||
294 | void mxr_layer_release(struct mxr_layer *layer); | ||
295 | |||
296 | int mxr_base_layer_register(struct mxr_layer *layer); | ||
297 | void mxr_base_layer_unregister(struct mxr_layer *layer); | ||
298 | |||
299 | unsigned long mxr_get_plane_size(const struct mxr_block *blk, | ||
300 | unsigned int width, unsigned int height); | ||
301 | |||
302 | /** adds new consumer for mixer's power */ | ||
303 | int __must_check mxr_power_get(struct mxr_device *mdev); | ||
304 | /** removes consumer for mixer's power */ | ||
305 | void mxr_power_put(struct mxr_device *mdev); | ||
306 | /** add new client for output configuration */ | ||
307 | void mxr_output_get(struct mxr_device *mdev); | ||
308 | /** removes new client for output configuration */ | ||
309 | void mxr_output_put(struct mxr_device *mdev); | ||
310 | /** add new client for streaming */ | ||
311 | void mxr_streamer_get(struct mxr_device *mdev); | ||
312 | /** removes new client for streaming */ | ||
313 | void mxr_streamer_put(struct mxr_device *mdev); | ||
314 | /** returns format of data delivared to current output */ | ||
315 | void mxr_get_mbus_fmt(struct mxr_device *mdev, | ||
316 | struct v4l2_mbus_framefmt *mbus_fmt); | ||
317 | |||
318 | /* Debug */ | ||
319 | |||
320 | #define mxr_err(mdev, fmt, ...) dev_err(mdev->dev, fmt, ##__VA_ARGS__) | ||
321 | #define mxr_warn(mdev, fmt, ...) dev_warn(mdev->dev, fmt, ##__VA_ARGS__) | ||
322 | #define mxr_info(mdev, fmt, ...) dev_info(mdev->dev, fmt, ##__VA_ARGS__) | ||
323 | |||
324 | #ifdef CONFIG_VIDEO_SAMSUNG_S5P_MIXER_DEBUG | ||
325 | #define mxr_dbg(mdev, fmt, ...) dev_dbg(mdev->dev, fmt, ##__VA_ARGS__) | ||
326 | #else | ||
327 | #define mxr_dbg(mdev, fmt, ...) do { (void) mdev; } while (0) | ||
328 | #endif | ||
329 | |||
330 | /* accessing Mixer's and Video Processor's registers */ | ||
331 | |||
332 | void mxr_vsync_set_update(struct mxr_device *mdev, int en); | ||
333 | void mxr_reg_reset(struct mxr_device *mdev); | ||
334 | irqreturn_t mxr_irq_handler(int irq, void *dev_data); | ||
335 | void mxr_reg_s_output(struct mxr_device *mdev, int cookie); | ||
336 | void mxr_reg_streamon(struct mxr_device *mdev); | ||
337 | void mxr_reg_streamoff(struct mxr_device *mdev); | ||
338 | int mxr_reg_wait4vsync(struct mxr_device *mdev); | ||
339 | void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, | ||
340 | struct v4l2_mbus_framefmt *fmt); | ||
341 | void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en); | ||
342 | void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr); | ||
343 | void mxr_reg_graph_format(struct mxr_device *mdev, int idx, | ||
344 | const struct mxr_format *fmt, const struct mxr_geometry *geo); | ||
345 | |||
346 | void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en); | ||
347 | void mxr_reg_vp_buffer(struct mxr_device *mdev, | ||
348 | dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]); | ||
349 | void mxr_reg_vp_format(struct mxr_device *mdev, | ||
350 | const struct mxr_format *fmt, const struct mxr_geometry *geo); | ||
351 | void mxr_reg_dump(struct mxr_device *mdev); | ||
352 | |||
353 | #endif /* SAMSUNG_MIXER_H */ | ||
354 | |||
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c new file mode 100644 index 00000000000..00643094b22 --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer_drv.c | |||
@@ -0,0 +1,487 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include "mixer.h" | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/fb.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/pm_runtime.h> | ||
24 | #include <linux/clk.h> | ||
25 | |||
26 | MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); | ||
27 | MODULE_DESCRIPTION("Samsung MIXER"); | ||
28 | MODULE_LICENSE("GPL"); | ||
29 | |||
30 | /* --------- DRIVER PARAMETERS ---------- */ | ||
31 | |||
32 | static struct mxr_output_conf mxr_output_conf[] = { | ||
33 | { | ||
34 | .output_name = "S5P HDMI connector", | ||
35 | .module_name = "s5p-hdmi", | ||
36 | .cookie = 1, | ||
37 | }, | ||
38 | { | ||
39 | .output_name = "S5P SDO connector", | ||
40 | .module_name = "s5p-sdo", | ||
41 | .cookie = 0, | ||
42 | }, | ||
43 | }; | ||
44 | |||
45 | void mxr_get_mbus_fmt(struct mxr_device *mdev, | ||
46 | struct v4l2_mbus_framefmt *mbus_fmt) | ||
47 | { | ||
48 | struct v4l2_subdev *sd; | ||
49 | int ret; | ||
50 | |||
51 | mutex_lock(&mdev->mutex); | ||
52 | sd = to_outsd(mdev); | ||
53 | ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt); | ||
54 | WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); | ||
55 | mutex_unlock(&mdev->mutex); | ||
56 | } | ||
57 | |||
58 | void mxr_streamer_get(struct mxr_device *mdev) | ||
59 | { | ||
60 | mutex_lock(&mdev->mutex); | ||
61 | ++mdev->n_streamer; | ||
62 | mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); | ||
63 | if (mdev->n_streamer == 1) { | ||
64 | struct v4l2_subdev *sd = to_outsd(mdev); | ||
65 | struct v4l2_mbus_framefmt mbus_fmt; | ||
66 | struct mxr_resources *res = &mdev->res; | ||
67 | int ret; | ||
68 | |||
69 | if (to_output(mdev)->cookie == 0) | ||
70 | clk_set_parent(res->sclk_mixer, res->sclk_dac); | ||
71 | else | ||
72 | clk_set_parent(res->sclk_mixer, res->sclk_hdmi); | ||
73 | mxr_reg_s_output(mdev, to_output(mdev)->cookie); | ||
74 | |||
75 | ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt); | ||
76 | WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name); | ||
77 | ret = v4l2_subdev_call(sd, video, s_stream, 1); | ||
78 | WARN(ret, "starting stream failed for output %s\n", sd->name); | ||
79 | |||
80 | mxr_reg_set_mbus_fmt(mdev, &mbus_fmt); | ||
81 | mxr_reg_streamon(mdev); | ||
82 | ret = mxr_reg_wait4vsync(mdev); | ||
83 | WARN(ret, "failed to get vsync (%d) from output\n", ret); | ||
84 | } | ||
85 | mutex_unlock(&mdev->mutex); | ||
86 | mxr_reg_dump(mdev); | ||
87 | /* FIXME: what to do when streaming fails? */ | ||
88 | } | ||
89 | |||
90 | void mxr_streamer_put(struct mxr_device *mdev) | ||
91 | { | ||
92 | mutex_lock(&mdev->mutex); | ||
93 | --mdev->n_streamer; | ||
94 | mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer); | ||
95 | if (mdev->n_streamer == 0) { | ||
96 | int ret; | ||
97 | struct v4l2_subdev *sd = to_outsd(mdev); | ||
98 | |||
99 | mxr_reg_streamoff(mdev); | ||
100 | /* vsync applies Mixer setup */ | ||
101 | ret = mxr_reg_wait4vsync(mdev); | ||
102 | WARN(ret, "failed to get vsync (%d) from output\n", ret); | ||
103 | ret = v4l2_subdev_call(sd, video, s_stream, 0); | ||
104 | WARN(ret, "stopping stream failed for output %s\n", sd->name); | ||
105 | } | ||
106 | WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n", | ||
107 | mdev->n_streamer); | ||
108 | mutex_unlock(&mdev->mutex); | ||
109 | mxr_reg_dump(mdev); | ||
110 | } | ||
111 | |||
112 | void mxr_output_get(struct mxr_device *mdev) | ||
113 | { | ||
114 | mutex_lock(&mdev->mutex); | ||
115 | ++mdev->n_output; | ||
116 | mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); | ||
117 | /* turn on auxiliary driver */ | ||
118 | if (mdev->n_output == 1) | ||
119 | v4l2_subdev_call(to_outsd(mdev), core, s_power, 1); | ||
120 | mutex_unlock(&mdev->mutex); | ||
121 | } | ||
122 | |||
123 | void mxr_output_put(struct mxr_device *mdev) | ||
124 | { | ||
125 | mutex_lock(&mdev->mutex); | ||
126 | --mdev->n_output; | ||
127 | mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output); | ||
128 | /* turn on auxiliary driver */ | ||
129 | if (mdev->n_output == 0) | ||
130 | v4l2_subdev_call(to_outsd(mdev), core, s_power, 0); | ||
131 | WARN(mdev->n_output < 0, "negative number of output users (%d)\n", | ||
132 | mdev->n_output); | ||
133 | mutex_unlock(&mdev->mutex); | ||
134 | } | ||
135 | |||
136 | int mxr_power_get(struct mxr_device *mdev) | ||
137 | { | ||
138 | int ret = pm_runtime_get_sync(mdev->dev); | ||
139 | |||
140 | /* returning 1 means that power is already enabled, | ||
141 | * so zero success be returned */ | ||
142 | if (IS_ERR_VALUE(ret)) | ||
143 | return ret; | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | void mxr_power_put(struct mxr_device *mdev) | ||
148 | { | ||
149 | pm_runtime_put_sync(mdev->dev); | ||
150 | } | ||
151 | |||
152 | /* --------- RESOURCE MANAGEMENT -------------*/ | ||
153 | |||
154 | static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev, | ||
155 | struct platform_device *pdev) | ||
156 | { | ||
157 | struct resource *res; | ||
158 | int ret; | ||
159 | |||
160 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr"); | ||
161 | if (res == NULL) { | ||
162 | mxr_err(mdev, "get memory resource failed.\n"); | ||
163 | ret = -ENXIO; | ||
164 | goto fail; | ||
165 | } | ||
166 | |||
167 | mdev->res.mxr_regs = ioremap(res->start, resource_size(res)); | ||
168 | if (mdev->res.mxr_regs == NULL) { | ||
169 | mxr_err(mdev, "register mapping failed.\n"); | ||
170 | ret = -ENXIO; | ||
171 | goto fail; | ||
172 | } | ||
173 | |||
174 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp"); | ||
175 | if (res == NULL) { | ||
176 | mxr_err(mdev, "get memory resource failed.\n"); | ||
177 | ret = -ENXIO; | ||
178 | goto fail_mxr_regs; | ||
179 | } | ||
180 | |||
181 | mdev->res.vp_regs = ioremap(res->start, resource_size(res)); | ||
182 | if (mdev->res.vp_regs == NULL) { | ||
183 | mxr_err(mdev, "register mapping failed.\n"); | ||
184 | ret = -ENXIO; | ||
185 | goto fail_mxr_regs; | ||
186 | } | ||
187 | |||
188 | res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq"); | ||
189 | if (res == NULL) { | ||
190 | mxr_err(mdev, "get interrupt resource failed.\n"); | ||
191 | ret = -ENXIO; | ||
192 | goto fail_vp_regs; | ||
193 | } | ||
194 | |||
195 | ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev); | ||
196 | if (ret) { | ||
197 | mxr_err(mdev, "request interrupt failed.\n"); | ||
198 | goto fail_vp_regs; | ||
199 | } | ||
200 | mdev->res.irq = res->start; | ||
201 | |||
202 | return 0; | ||
203 | |||
204 | fail_vp_regs: | ||
205 | iounmap(mdev->res.vp_regs); | ||
206 | |||
207 | fail_mxr_regs: | ||
208 | iounmap(mdev->res.mxr_regs); | ||
209 | |||
210 | fail: | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | static void mxr_release_plat_resources(struct mxr_device *mdev) | ||
215 | { | ||
216 | free_irq(mdev->res.irq, mdev); | ||
217 | iounmap(mdev->res.vp_regs); | ||
218 | iounmap(mdev->res.mxr_regs); | ||
219 | } | ||
220 | |||
221 | static void mxr_release_clocks(struct mxr_device *mdev) | ||
222 | { | ||
223 | struct mxr_resources *res = &mdev->res; | ||
224 | |||
225 | if (!IS_ERR_OR_NULL(res->sclk_dac)) | ||
226 | clk_put(res->sclk_dac); | ||
227 | if (!IS_ERR_OR_NULL(res->sclk_hdmi)) | ||
228 | clk_put(res->sclk_hdmi); | ||
229 | if (!IS_ERR_OR_NULL(res->sclk_mixer)) | ||
230 | clk_put(res->sclk_mixer); | ||
231 | if (!IS_ERR_OR_NULL(res->vp)) | ||
232 | clk_put(res->vp); | ||
233 | if (!IS_ERR_OR_NULL(res->mixer)) | ||
234 | clk_put(res->mixer); | ||
235 | } | ||
236 | |||
237 | static int mxr_acquire_clocks(struct mxr_device *mdev) | ||
238 | { | ||
239 | struct mxr_resources *res = &mdev->res; | ||
240 | struct device *dev = mdev->dev; | ||
241 | |||
242 | res->mixer = clk_get(dev, "mixer"); | ||
243 | if (IS_ERR_OR_NULL(res->mixer)) { | ||
244 | mxr_err(mdev, "failed to get clock 'mixer'\n"); | ||
245 | goto fail; | ||
246 | } | ||
247 | res->vp = clk_get(dev, "vp"); | ||
248 | if (IS_ERR_OR_NULL(res->vp)) { | ||
249 | mxr_err(mdev, "failed to get clock 'vp'\n"); | ||
250 | goto fail; | ||
251 | } | ||
252 | res->sclk_mixer = clk_get(dev, "sclk_mixer"); | ||
253 | if (IS_ERR_OR_NULL(res->sclk_mixer)) { | ||
254 | mxr_err(mdev, "failed to get clock 'sclk_mixer'\n"); | ||
255 | goto fail; | ||
256 | } | ||
257 | res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); | ||
258 | if (IS_ERR_OR_NULL(res->sclk_hdmi)) { | ||
259 | mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n"); | ||
260 | goto fail; | ||
261 | } | ||
262 | res->sclk_dac = clk_get(dev, "sclk_dac"); | ||
263 | if (IS_ERR_OR_NULL(res->sclk_dac)) { | ||
264 | mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); | ||
265 | goto fail; | ||
266 | } | ||
267 | |||
268 | return 0; | ||
269 | fail: | ||
270 | mxr_release_clocks(mdev); | ||
271 | return -ENODEV; | ||
272 | } | ||
273 | |||
274 | static int __devinit mxr_acquire_resources(struct mxr_device *mdev, | ||
275 | struct platform_device *pdev) | ||
276 | { | ||
277 | int ret; | ||
278 | ret = mxr_acquire_plat_resources(mdev, pdev); | ||
279 | |||
280 | if (ret) | ||
281 | goto fail; | ||
282 | |||
283 | ret = mxr_acquire_clocks(mdev); | ||
284 | if (ret) | ||
285 | goto fail_plat; | ||
286 | |||
287 | mxr_info(mdev, "resources acquired\n"); | ||
288 | return 0; | ||
289 | |||
290 | fail_plat: | ||
291 | mxr_release_plat_resources(mdev); | ||
292 | fail: | ||
293 | mxr_err(mdev, "resources acquire failed\n"); | ||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | static void mxr_release_resources(struct mxr_device *mdev) | ||
298 | { | ||
299 | mxr_release_clocks(mdev); | ||
300 | mxr_release_plat_resources(mdev); | ||
301 | memset(&mdev->res, 0, sizeof mdev->res); | ||
302 | } | ||
303 | |||
304 | static void mxr_release_layers(struct mxr_device *mdev) | ||
305 | { | ||
306 | int i; | ||
307 | |||
308 | for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i) | ||
309 | if (mdev->layer[i]) | ||
310 | mxr_layer_release(mdev->layer[i]); | ||
311 | } | ||
312 | |||
313 | static int __devinit mxr_acquire_layers(struct mxr_device *mdev, | ||
314 | struct mxr_platform_data *pdata) | ||
315 | { | ||
316 | mdev->layer[0] = mxr_graph_layer_create(mdev, 0); | ||
317 | mdev->layer[1] = mxr_graph_layer_create(mdev, 1); | ||
318 | mdev->layer[2] = mxr_vp_layer_create(mdev, 0); | ||
319 | |||
320 | if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) { | ||
321 | mxr_err(mdev, "failed to acquire layers\n"); | ||
322 | goto fail; | ||
323 | } | ||
324 | |||
325 | return 0; | ||
326 | |||
327 | fail: | ||
328 | mxr_release_layers(mdev); | ||
329 | return -ENODEV; | ||
330 | } | ||
331 | |||
332 | /* ---------- POWER MANAGEMENT ----------- */ | ||
333 | |||
334 | static int mxr_runtime_resume(struct device *dev) | ||
335 | { | ||
336 | struct mxr_device *mdev = to_mdev(dev); | ||
337 | struct mxr_resources *res = &mdev->res; | ||
338 | |||
339 | mxr_dbg(mdev, "resume - start\n"); | ||
340 | mutex_lock(&mdev->mutex); | ||
341 | /* turn clocks on */ | ||
342 | clk_enable(res->mixer); | ||
343 | clk_enable(res->vp); | ||
344 | clk_enable(res->sclk_mixer); | ||
345 | /* apply default configuration */ | ||
346 | mxr_reg_reset(mdev); | ||
347 | mxr_dbg(mdev, "resume - finished\n"); | ||
348 | |||
349 | mutex_unlock(&mdev->mutex); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int mxr_runtime_suspend(struct device *dev) | ||
354 | { | ||
355 | struct mxr_device *mdev = to_mdev(dev); | ||
356 | struct mxr_resources *res = &mdev->res; | ||
357 | mxr_dbg(mdev, "suspend - start\n"); | ||
358 | mutex_lock(&mdev->mutex); | ||
359 | /* turn clocks off */ | ||
360 | clk_disable(res->sclk_mixer); | ||
361 | clk_disable(res->vp); | ||
362 | clk_disable(res->mixer); | ||
363 | mutex_unlock(&mdev->mutex); | ||
364 | mxr_dbg(mdev, "suspend - finished\n"); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static const struct dev_pm_ops mxr_pm_ops = { | ||
369 | .runtime_suspend = mxr_runtime_suspend, | ||
370 | .runtime_resume = mxr_runtime_resume, | ||
371 | }; | ||
372 | |||
373 | /* --------- DRIVER INITIALIZATION ---------- */ | ||
374 | |||
375 | static int __devinit mxr_probe(struct platform_device *pdev) | ||
376 | { | ||
377 | struct device *dev = &pdev->dev; | ||
378 | struct mxr_platform_data *pdata = dev->platform_data; | ||
379 | struct mxr_device *mdev; | ||
380 | int ret; | ||
381 | |||
382 | /* mdev does not exist yet so no mxr_dbg is used */ | ||
383 | dev_info(dev, "probe start\n"); | ||
384 | |||
385 | mdev = kzalloc(sizeof *mdev, GFP_KERNEL); | ||
386 | if (!mdev) { | ||
387 | mxr_err(mdev, "not enough memory.\n"); | ||
388 | ret = -ENOMEM; | ||
389 | goto fail; | ||
390 | } | ||
391 | |||
392 | /* setup pointer to master device */ | ||
393 | mdev->dev = dev; | ||
394 | |||
395 | mutex_init(&mdev->mutex); | ||
396 | spin_lock_init(&mdev->reg_slock); | ||
397 | init_waitqueue_head(&mdev->event_queue); | ||
398 | |||
399 | /* acquire resources: regs, irqs, clocks, regulators */ | ||
400 | ret = mxr_acquire_resources(mdev, pdev); | ||
401 | if (ret) | ||
402 | goto fail_mem; | ||
403 | |||
404 | /* configure resources for video output */ | ||
405 | ret = mxr_acquire_video(mdev, mxr_output_conf, | ||
406 | ARRAY_SIZE(mxr_output_conf)); | ||
407 | if (ret) | ||
408 | goto fail_resources; | ||
409 | |||
410 | /* configure layers */ | ||
411 | ret = mxr_acquire_layers(mdev, pdata); | ||
412 | if (ret) | ||
413 | goto fail_video; | ||
414 | |||
415 | pm_runtime_enable(dev); | ||
416 | |||
417 | mxr_info(mdev, "probe successful\n"); | ||
418 | return 0; | ||
419 | |||
420 | fail_video: | ||
421 | mxr_release_video(mdev); | ||
422 | |||
423 | fail_resources: | ||
424 | mxr_release_resources(mdev); | ||
425 | |||
426 | fail_mem: | ||
427 | kfree(mdev); | ||
428 | |||
429 | fail: | ||
430 | dev_info(dev, "probe failed\n"); | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | static int __devexit mxr_remove(struct platform_device *pdev) | ||
435 | { | ||
436 | struct device *dev = &pdev->dev; | ||
437 | struct mxr_device *mdev = to_mdev(dev); | ||
438 | |||
439 | pm_runtime_disable(dev); | ||
440 | |||
441 | mxr_release_layers(mdev); | ||
442 | mxr_release_video(mdev); | ||
443 | mxr_release_resources(mdev); | ||
444 | |||
445 | kfree(mdev); | ||
446 | |||
447 | dev_info(dev, "remove sucessful\n"); | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static struct platform_driver mxr_driver __refdata = { | ||
452 | .probe = mxr_probe, | ||
453 | .remove = __devexit_p(mxr_remove), | ||
454 | .driver = { | ||
455 | .name = MXR_DRIVER_NAME, | ||
456 | .owner = THIS_MODULE, | ||
457 | .pm = &mxr_pm_ops, | ||
458 | } | ||
459 | }; | ||
460 | |||
461 | static int __init mxr_init(void) | ||
462 | { | ||
463 | int i, ret; | ||
464 | static const char banner[] __initdata = KERN_INFO | ||
465 | "Samsung TV Mixer driver, " | ||
466 | "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; | ||
467 | printk(banner); | ||
468 | |||
469 | /* Loading auxiliary modules */ | ||
470 | for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i) | ||
471 | request_module(mxr_output_conf[i].module_name); | ||
472 | |||
473 | ret = platform_driver_register(&mxr_driver); | ||
474 | if (ret != 0) { | ||
475 | printk(KERN_ERR "registration of MIXER driver failed\n"); | ||
476 | return -ENXIO; | ||
477 | } | ||
478 | |||
479 | return 0; | ||
480 | } | ||
481 | module_init(mxr_init); | ||
482 | |||
483 | static void __exit mxr_exit(void) | ||
484 | { | ||
485 | platform_driver_unregister(&mxr_driver); | ||
486 | } | ||
487 | module_exit(mxr_exit); | ||
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/video/s5p-tv/mixer_grp_layer.c new file mode 100644 index 00000000000..58f0ba49580 --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer_grp_layer.c | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include "mixer.h" | ||
15 | |||
16 | #include <media/videobuf2-dma-contig.h> | ||
17 | |||
18 | /* FORMAT DEFINITIONS */ | ||
19 | |||
20 | static const struct mxr_format mxr_fb_fmt_rgb565 = { | ||
21 | .name = "RGB565", | ||
22 | .fourcc = V4L2_PIX_FMT_RGB565, | ||
23 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
24 | .num_planes = 1, | ||
25 | .plane = { | ||
26 | { .width = 1, .height = 1, .size = 2 }, | ||
27 | }, | ||
28 | .num_subframes = 1, | ||
29 | .cookie = 4, | ||
30 | }; | ||
31 | |||
32 | static const struct mxr_format mxr_fb_fmt_argb1555 = { | ||
33 | .name = "ARGB1555", | ||
34 | .num_planes = 1, | ||
35 | .fourcc = V4L2_PIX_FMT_RGB555, | ||
36 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
37 | .plane = { | ||
38 | { .width = 1, .height = 1, .size = 2 }, | ||
39 | }, | ||
40 | .num_subframes = 1, | ||
41 | .cookie = 5, | ||
42 | }; | ||
43 | |||
44 | static const struct mxr_format mxr_fb_fmt_argb4444 = { | ||
45 | .name = "ARGB4444", | ||
46 | .num_planes = 1, | ||
47 | .fourcc = V4L2_PIX_FMT_RGB444, | ||
48 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
49 | .plane = { | ||
50 | { .width = 1, .height = 1, .size = 2 }, | ||
51 | }, | ||
52 | .num_subframes = 1, | ||
53 | .cookie = 6, | ||
54 | }; | ||
55 | |||
56 | static const struct mxr_format mxr_fb_fmt_argb8888 = { | ||
57 | .name = "ARGB8888", | ||
58 | .fourcc = V4L2_PIX_FMT_BGR32, | ||
59 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
60 | .num_planes = 1, | ||
61 | .plane = { | ||
62 | { .width = 1, .height = 1, .size = 4 }, | ||
63 | }, | ||
64 | .num_subframes = 1, | ||
65 | .cookie = 7, | ||
66 | }; | ||
67 | |||
68 | static const struct mxr_format *mxr_graph_format[] = { | ||
69 | &mxr_fb_fmt_rgb565, | ||
70 | &mxr_fb_fmt_argb1555, | ||
71 | &mxr_fb_fmt_argb4444, | ||
72 | &mxr_fb_fmt_argb8888, | ||
73 | }; | ||
74 | |||
75 | /* AUXILIARY CALLBACKS */ | ||
76 | |||
77 | static void mxr_graph_layer_release(struct mxr_layer *layer) | ||
78 | { | ||
79 | mxr_base_layer_unregister(layer); | ||
80 | mxr_base_layer_release(layer); | ||
81 | } | ||
82 | |||
83 | static void mxr_graph_buffer_set(struct mxr_layer *layer, | ||
84 | struct mxr_buffer *buf) | ||
85 | { | ||
86 | dma_addr_t addr = 0; | ||
87 | |||
88 | if (buf) | ||
89 | addr = vb2_dma_contig_plane_paddr(&buf->vb, 0); | ||
90 | mxr_reg_graph_buffer(layer->mdev, layer->idx, addr); | ||
91 | } | ||
92 | |||
93 | static void mxr_graph_stream_set(struct mxr_layer *layer, int en) | ||
94 | { | ||
95 | mxr_reg_graph_layer_stream(layer->mdev, layer->idx, en); | ||
96 | } | ||
97 | |||
98 | static void mxr_graph_format_set(struct mxr_layer *layer) | ||
99 | { | ||
100 | mxr_reg_graph_format(layer->mdev, layer->idx, | ||
101 | layer->fmt, &layer->geo); | ||
102 | } | ||
103 | |||
104 | static void mxr_graph_fix_geometry(struct mxr_layer *layer) | ||
105 | { | ||
106 | struct mxr_geometry *geo = &layer->geo; | ||
107 | |||
108 | /* limit to boundary size */ | ||
109 | geo->src.full_width = clamp_val(geo->src.full_width, 1, 32767); | ||
110 | geo->src.full_height = clamp_val(geo->src.full_height, 1, 2047); | ||
111 | geo->src.width = clamp_val(geo->src.width, 1, geo->src.full_width); | ||
112 | geo->src.width = min(geo->src.width, 2047U); | ||
113 | /* not possible to crop of Y axis */ | ||
114 | geo->src.y_offset = min(geo->src.y_offset, geo->src.full_height - 1); | ||
115 | geo->src.height = geo->src.full_height - geo->src.y_offset; | ||
116 | /* limitting offset */ | ||
117 | geo->src.x_offset = min(geo->src.x_offset, | ||
118 | geo->src.full_width - geo->src.width); | ||
119 | |||
120 | /* setting position in output */ | ||
121 | geo->dst.width = min(geo->dst.width, geo->dst.full_width); | ||
122 | geo->dst.height = min(geo->dst.height, geo->dst.full_height); | ||
123 | |||
124 | /* Mixer supports only 1x and 2x scaling */ | ||
125 | if (geo->dst.width >= 2 * geo->src.width) { | ||
126 | geo->x_ratio = 1; | ||
127 | geo->dst.width = 2 * geo->src.width; | ||
128 | } else { | ||
129 | geo->x_ratio = 0; | ||
130 | geo->dst.width = geo->src.width; | ||
131 | } | ||
132 | |||
133 | if (geo->dst.height >= 2 * geo->src.height) { | ||
134 | geo->y_ratio = 1; | ||
135 | geo->dst.height = 2 * geo->src.height; | ||
136 | } else { | ||
137 | geo->y_ratio = 0; | ||
138 | geo->dst.height = geo->src.height; | ||
139 | } | ||
140 | |||
141 | geo->dst.x_offset = min(geo->dst.x_offset, | ||
142 | geo->dst.full_width - geo->dst.width); | ||
143 | geo->dst.y_offset = min(geo->dst.y_offset, | ||
144 | geo->dst.full_height - geo->dst.height); | ||
145 | } | ||
146 | |||
147 | /* PUBLIC API */ | ||
148 | |||
149 | struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx) | ||
150 | { | ||
151 | struct mxr_layer *layer; | ||
152 | int ret; | ||
153 | struct mxr_layer_ops ops = { | ||
154 | .release = mxr_graph_layer_release, | ||
155 | .buffer_set = mxr_graph_buffer_set, | ||
156 | .stream_set = mxr_graph_stream_set, | ||
157 | .format_set = mxr_graph_format_set, | ||
158 | .fix_geometry = mxr_graph_fix_geometry, | ||
159 | }; | ||
160 | char name[32]; | ||
161 | |||
162 | sprintf(name, "graph%d", idx); | ||
163 | |||
164 | layer = mxr_base_layer_create(mdev, idx, name, &ops); | ||
165 | if (layer == NULL) { | ||
166 | mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); | ||
167 | goto fail; | ||
168 | } | ||
169 | |||
170 | layer->fmt_array = mxr_graph_format; | ||
171 | layer->fmt_array_size = ARRAY_SIZE(mxr_graph_format); | ||
172 | |||
173 | ret = mxr_base_layer_register(layer); | ||
174 | if (ret) | ||
175 | goto fail_layer; | ||
176 | |||
177 | return layer; | ||
178 | |||
179 | fail_layer: | ||
180 | mxr_base_layer_release(layer); | ||
181 | |||
182 | fail: | ||
183 | return NULL; | ||
184 | } | ||
185 | |||
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c new file mode 100644 index 00000000000..38dac672aa1 --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer_reg.c | |||
@@ -0,0 +1,541 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include "mixer.h" | ||
15 | #include "regs-mixer.h" | ||
16 | #include "regs-vp.h" | ||
17 | |||
18 | #include <linux/delay.h> | ||
19 | |||
20 | /* Register access subroutines */ | ||
21 | |||
22 | static inline u32 vp_read(struct mxr_device *mdev, u32 reg_id) | ||
23 | { | ||
24 | return readl(mdev->res.vp_regs + reg_id); | ||
25 | } | ||
26 | |||
27 | static inline void vp_write(struct mxr_device *mdev, u32 reg_id, u32 val) | ||
28 | { | ||
29 | writel(val, mdev->res.vp_regs + reg_id); | ||
30 | } | ||
31 | |||
32 | static inline void vp_write_mask(struct mxr_device *mdev, u32 reg_id, | ||
33 | u32 val, u32 mask) | ||
34 | { | ||
35 | u32 old = vp_read(mdev, reg_id); | ||
36 | |||
37 | val = (val & mask) | (old & ~mask); | ||
38 | writel(val, mdev->res.vp_regs + reg_id); | ||
39 | } | ||
40 | |||
41 | static inline u32 mxr_read(struct mxr_device *mdev, u32 reg_id) | ||
42 | { | ||
43 | return readl(mdev->res.mxr_regs + reg_id); | ||
44 | } | ||
45 | |||
46 | static inline void mxr_write(struct mxr_device *mdev, u32 reg_id, u32 val) | ||
47 | { | ||
48 | writel(val, mdev->res.mxr_regs + reg_id); | ||
49 | } | ||
50 | |||
51 | static inline void mxr_write_mask(struct mxr_device *mdev, u32 reg_id, | ||
52 | u32 val, u32 mask) | ||
53 | { | ||
54 | u32 old = mxr_read(mdev, reg_id); | ||
55 | |||
56 | val = (val & mask) | (old & ~mask); | ||
57 | writel(val, mdev->res.mxr_regs + reg_id); | ||
58 | } | ||
59 | |||
60 | void mxr_vsync_set_update(struct mxr_device *mdev, int en) | ||
61 | { | ||
62 | /* block update on vsync */ | ||
63 | mxr_write_mask(mdev, MXR_STATUS, en ? MXR_STATUS_SYNC_ENABLE : 0, | ||
64 | MXR_STATUS_SYNC_ENABLE); | ||
65 | vp_write(mdev, VP_SHADOW_UPDATE, en ? VP_SHADOW_UPDATE_ENABLE : 0); | ||
66 | } | ||
67 | |||
68 | static void __mxr_reg_vp_reset(struct mxr_device *mdev) | ||
69 | { | ||
70 | int tries = 100; | ||
71 | |||
72 | vp_write(mdev, VP_SRESET, VP_SRESET_PROCESSING); | ||
73 | for (tries = 100; tries; --tries) { | ||
74 | /* waiting until VP_SRESET_PROCESSING is 0 */ | ||
75 | if (~vp_read(mdev, VP_SRESET) & VP_SRESET_PROCESSING) | ||
76 | break; | ||
77 | mdelay(10); | ||
78 | } | ||
79 | WARN(tries == 0, "failed to reset Video Processor\n"); | ||
80 | } | ||
81 | |||
82 | static void mxr_reg_vp_default_filter(struct mxr_device *mdev); | ||
83 | |||
84 | void mxr_reg_reset(struct mxr_device *mdev) | ||
85 | { | ||
86 | unsigned long flags; | ||
87 | u32 val; /* value stored to register */ | ||
88 | |||
89 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
90 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
91 | |||
92 | /* set output in RGB888 mode */ | ||
93 | mxr_write(mdev, MXR_CFG, MXR_CFG_OUT_YUV444); | ||
94 | |||
95 | /* 16 beat burst in DMA */ | ||
96 | mxr_write_mask(mdev, MXR_STATUS, MXR_STATUS_16_BURST, | ||
97 | MXR_STATUS_BURST_MASK); | ||
98 | |||
99 | /* setting default layer priority: layer1 > video > layer0 | ||
100 | * because typical usage scenario would be | ||
101 | * layer0 - framebuffer | ||
102 | * video - video overlay | ||
103 | * layer1 - OSD | ||
104 | */ | ||
105 | val = MXR_LAYER_CFG_GRP0_VAL(1); | ||
106 | val |= MXR_LAYER_CFG_VP_VAL(2); | ||
107 | val |= MXR_LAYER_CFG_GRP1_VAL(3); | ||
108 | mxr_write(mdev, MXR_LAYER_CFG, val); | ||
109 | |||
110 | /* use dark gray background color */ | ||
111 | mxr_write(mdev, MXR_BG_COLOR0, 0x808080); | ||
112 | mxr_write(mdev, MXR_BG_COLOR1, 0x808080); | ||
113 | mxr_write(mdev, MXR_BG_COLOR2, 0x808080); | ||
114 | |||
115 | /* setting graphical layers */ | ||
116 | |||
117 | val = MXR_GRP_CFG_COLOR_KEY_DISABLE; /* no blank key */ | ||
118 | val |= MXR_GRP_CFG_BLEND_PRE_MUL; /* premul mode */ | ||
119 | val |= MXR_GRP_CFG_ALPHA_VAL(0xff); /* non-transparent alpha */ | ||
120 | |||
121 | /* the same configuration for both layers */ | ||
122 | mxr_write(mdev, MXR_GRAPHIC_CFG(0), val); | ||
123 | mxr_write(mdev, MXR_GRAPHIC_CFG(1), val); | ||
124 | |||
125 | /* configuration of Video Processor Registers */ | ||
126 | __mxr_reg_vp_reset(mdev); | ||
127 | mxr_reg_vp_default_filter(mdev); | ||
128 | |||
129 | /* enable all interrupts */ | ||
130 | mxr_write_mask(mdev, MXR_INT_EN, ~0, MXR_INT_EN_ALL); | ||
131 | |||
132 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
133 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
134 | } | ||
135 | |||
136 | void mxr_reg_graph_format(struct mxr_device *mdev, int idx, | ||
137 | const struct mxr_format *fmt, const struct mxr_geometry *geo) | ||
138 | { | ||
139 | u32 val; | ||
140 | unsigned long flags; | ||
141 | |||
142 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
143 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
144 | |||
145 | /* setup format */ | ||
146 | mxr_write_mask(mdev, MXR_GRAPHIC_CFG(idx), | ||
147 | MXR_GRP_CFG_FORMAT_VAL(fmt->cookie), MXR_GRP_CFG_FORMAT_MASK); | ||
148 | |||
149 | /* setup geometry */ | ||
150 | mxr_write(mdev, MXR_GRAPHIC_SPAN(idx), geo->src.full_width); | ||
151 | val = MXR_GRP_WH_WIDTH(geo->src.width); | ||
152 | val |= MXR_GRP_WH_HEIGHT(geo->src.height); | ||
153 | val |= MXR_GRP_WH_H_SCALE(geo->x_ratio); | ||
154 | val |= MXR_GRP_WH_V_SCALE(geo->y_ratio); | ||
155 | mxr_write(mdev, MXR_GRAPHIC_WH(idx), val); | ||
156 | |||
157 | /* setup offsets in source image */ | ||
158 | val = MXR_GRP_SXY_SX(geo->src.x_offset); | ||
159 | val |= MXR_GRP_SXY_SY(geo->src.y_offset); | ||
160 | mxr_write(mdev, MXR_GRAPHIC_SXY(idx), val); | ||
161 | |||
162 | /* setup offsets in display image */ | ||
163 | val = MXR_GRP_DXY_DX(geo->dst.x_offset); | ||
164 | val |= MXR_GRP_DXY_DY(geo->dst.y_offset); | ||
165 | mxr_write(mdev, MXR_GRAPHIC_DXY(idx), val); | ||
166 | |||
167 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
168 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
169 | } | ||
170 | |||
171 | void mxr_reg_vp_format(struct mxr_device *mdev, | ||
172 | const struct mxr_format *fmt, const struct mxr_geometry *geo) | ||
173 | { | ||
174 | unsigned long flags; | ||
175 | |||
176 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
177 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
178 | |||
179 | vp_write_mask(mdev, VP_MODE, fmt->cookie, VP_MODE_FMT_MASK); | ||
180 | |||
181 | /* setting size of input image */ | ||
182 | vp_write(mdev, VP_IMG_SIZE_Y, VP_IMG_HSIZE(geo->src.full_width) | | ||
183 | VP_IMG_VSIZE(geo->src.full_height)); | ||
184 | /* chroma height has to reduced by 2 to avoid chroma distorions */ | ||
185 | vp_write(mdev, VP_IMG_SIZE_C, VP_IMG_HSIZE(geo->src.full_width) | | ||
186 | VP_IMG_VSIZE(geo->src.full_height / 2)); | ||
187 | |||
188 | vp_write(mdev, VP_SRC_WIDTH, geo->src.width); | ||
189 | vp_write(mdev, VP_SRC_HEIGHT, geo->src.height); | ||
190 | vp_write(mdev, VP_SRC_H_POSITION, | ||
191 | VP_SRC_H_POSITION_VAL(geo->src.x_offset)); | ||
192 | vp_write(mdev, VP_SRC_V_POSITION, geo->src.y_offset); | ||
193 | |||
194 | vp_write(mdev, VP_DST_WIDTH, geo->dst.width); | ||
195 | vp_write(mdev, VP_DST_H_POSITION, geo->dst.x_offset); | ||
196 | if (geo->dst.field == V4L2_FIELD_INTERLACED) { | ||
197 | vp_write(mdev, VP_DST_HEIGHT, geo->dst.height / 2); | ||
198 | vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset / 2); | ||
199 | } else { | ||
200 | vp_write(mdev, VP_DST_HEIGHT, geo->dst.height); | ||
201 | vp_write(mdev, VP_DST_V_POSITION, geo->dst.y_offset); | ||
202 | } | ||
203 | |||
204 | vp_write(mdev, VP_H_RATIO, geo->x_ratio); | ||
205 | vp_write(mdev, VP_V_RATIO, geo->y_ratio); | ||
206 | |||
207 | vp_write(mdev, VP_ENDIAN_MODE, VP_ENDIAN_MODE_LITTLE); | ||
208 | |||
209 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
210 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
211 | |||
212 | } | ||
213 | |||
214 | void mxr_reg_graph_buffer(struct mxr_device *mdev, int idx, dma_addr_t addr) | ||
215 | { | ||
216 | u32 val = addr ? ~0 : 0; | ||
217 | unsigned long flags; | ||
218 | |||
219 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
220 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
221 | |||
222 | if (idx == 0) | ||
223 | mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP0_ENABLE); | ||
224 | else | ||
225 | mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_GRP1_ENABLE); | ||
226 | mxr_write(mdev, MXR_GRAPHIC_BASE(idx), addr); | ||
227 | |||
228 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
229 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
230 | } | ||
231 | |||
232 | void mxr_reg_vp_buffer(struct mxr_device *mdev, | ||
233 | dma_addr_t luma_addr[2], dma_addr_t chroma_addr[2]) | ||
234 | { | ||
235 | u32 val = luma_addr[0] ? ~0 : 0; | ||
236 | unsigned long flags; | ||
237 | |||
238 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
239 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
240 | |||
241 | mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_VP_ENABLE); | ||
242 | vp_write_mask(mdev, VP_ENABLE, val, VP_ENABLE_ON); | ||
243 | /* TODO: fix tiled mode */ | ||
244 | vp_write(mdev, VP_TOP_Y_PTR, luma_addr[0]); | ||
245 | vp_write(mdev, VP_TOP_C_PTR, chroma_addr[0]); | ||
246 | vp_write(mdev, VP_BOT_Y_PTR, luma_addr[1]); | ||
247 | vp_write(mdev, VP_BOT_C_PTR, chroma_addr[1]); | ||
248 | |||
249 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
250 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
251 | } | ||
252 | |||
253 | static void mxr_irq_layer_handle(struct mxr_layer *layer) | ||
254 | { | ||
255 | struct list_head *head = &layer->enq_list; | ||
256 | struct mxr_buffer *done; | ||
257 | |||
258 | /* skip non-existing layer */ | ||
259 | if (layer == NULL) | ||
260 | return; | ||
261 | |||
262 | spin_lock(&layer->enq_slock); | ||
263 | if (layer->state == MXR_LAYER_IDLE) | ||
264 | goto done; | ||
265 | |||
266 | done = layer->shadow_buf; | ||
267 | layer->shadow_buf = layer->update_buf; | ||
268 | |||
269 | if (list_empty(head)) { | ||
270 | if (layer->state != MXR_LAYER_STREAMING) | ||
271 | layer->update_buf = NULL; | ||
272 | } else { | ||
273 | struct mxr_buffer *next; | ||
274 | next = list_first_entry(head, struct mxr_buffer, list); | ||
275 | list_del(&next->list); | ||
276 | layer->update_buf = next; | ||
277 | } | ||
278 | |||
279 | layer->ops.buffer_set(layer, layer->update_buf); | ||
280 | |||
281 | if (done && done != layer->shadow_buf) | ||
282 | vb2_buffer_done(&done->vb, VB2_BUF_STATE_DONE); | ||
283 | |||
284 | done: | ||
285 | spin_unlock(&layer->enq_slock); | ||
286 | } | ||
287 | |||
288 | irqreturn_t mxr_irq_handler(int irq, void *dev_data) | ||
289 | { | ||
290 | struct mxr_device *mdev = dev_data; | ||
291 | u32 i, val; | ||
292 | |||
293 | spin_lock(&mdev->reg_slock); | ||
294 | val = mxr_read(mdev, MXR_INT_STATUS); | ||
295 | |||
296 | /* wake up process waiting for VSYNC */ | ||
297 | if (val & MXR_INT_STATUS_VSYNC) { | ||
298 | set_bit(MXR_EVENT_VSYNC, &mdev->event_flags); | ||
299 | wake_up(&mdev->event_queue); | ||
300 | } | ||
301 | |||
302 | /* clear interrupts */ | ||
303 | if (~val & MXR_INT_EN_VSYNC) { | ||
304 | /* vsync interrupt use different bit for read and clear */ | ||
305 | val &= ~MXR_INT_EN_VSYNC; | ||
306 | val |= MXR_INT_CLEAR_VSYNC; | ||
307 | } | ||
308 | mxr_write(mdev, MXR_INT_STATUS, val); | ||
309 | |||
310 | spin_unlock(&mdev->reg_slock); | ||
311 | /* leave on non-vsync event */ | ||
312 | if (~val & MXR_INT_CLEAR_VSYNC) | ||
313 | return IRQ_HANDLED; | ||
314 | for (i = 0; i < MXR_MAX_LAYERS; ++i) | ||
315 | mxr_irq_layer_handle(mdev->layer[i]); | ||
316 | return IRQ_HANDLED; | ||
317 | } | ||
318 | |||
319 | void mxr_reg_s_output(struct mxr_device *mdev, int cookie) | ||
320 | { | ||
321 | u32 val; | ||
322 | |||
323 | val = cookie == 0 ? MXR_CFG_DST_SDO : MXR_CFG_DST_HDMI; | ||
324 | mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_DST_MASK); | ||
325 | } | ||
326 | |||
327 | void mxr_reg_streamon(struct mxr_device *mdev) | ||
328 | { | ||
329 | unsigned long flags; | ||
330 | |||
331 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
332 | /* single write -> no need to block vsync update */ | ||
333 | |||
334 | /* start MIXER */ | ||
335 | mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); | ||
336 | |||
337 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
338 | } | ||
339 | |||
340 | void mxr_reg_streamoff(struct mxr_device *mdev) | ||
341 | { | ||
342 | unsigned long flags; | ||
343 | |||
344 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
345 | /* single write -> no need to block vsync update */ | ||
346 | |||
347 | /* stop MIXER */ | ||
348 | mxr_write_mask(mdev, MXR_STATUS, 0, MXR_STATUS_REG_RUN); | ||
349 | |||
350 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
351 | } | ||
352 | |||
353 | int mxr_reg_wait4vsync(struct mxr_device *mdev) | ||
354 | { | ||
355 | int ret; | ||
356 | |||
357 | clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags); | ||
358 | /* TODO: consider adding interruptible */ | ||
359 | ret = wait_event_timeout(mdev->event_queue, | ||
360 | test_bit(MXR_EVENT_VSYNC, &mdev->event_flags), | ||
361 | msecs_to_jiffies(1000)); | ||
362 | if (ret > 0) | ||
363 | return 0; | ||
364 | if (ret < 0) | ||
365 | return ret; | ||
366 | mxr_warn(mdev, "no vsync detected - timeout\n"); | ||
367 | return -ETIME; | ||
368 | } | ||
369 | |||
370 | void mxr_reg_set_mbus_fmt(struct mxr_device *mdev, | ||
371 | struct v4l2_mbus_framefmt *fmt) | ||
372 | { | ||
373 | u32 val = 0; | ||
374 | unsigned long flags; | ||
375 | |||
376 | spin_lock_irqsave(&mdev->reg_slock, flags); | ||
377 | mxr_vsync_set_update(mdev, MXR_DISABLE); | ||
378 | |||
379 | /* choosing between interlace and progressive mode */ | ||
380 | if (fmt->field == V4L2_FIELD_INTERLACED) | ||
381 | val |= MXR_CFG_SCAN_INTERLACE; | ||
382 | else | ||
383 | val |= MXR_CFG_SCAN_PROGRASSIVE; | ||
384 | |||
385 | /* choosing between porper HD and SD mode */ | ||
386 | if (fmt->height == 480) | ||
387 | val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; | ||
388 | else if (fmt->height == 576) | ||
389 | val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; | ||
390 | else if (fmt->height == 720) | ||
391 | val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; | ||
392 | else if (fmt->height == 1080) | ||
393 | val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; | ||
394 | else | ||
395 | WARN(1, "unrecognized mbus height %u!\n", fmt->height); | ||
396 | |||
397 | mxr_write_mask(mdev, MXR_CFG, val, MXR_CFG_SCAN_MASK); | ||
398 | |||
399 | val = (fmt->field == V4L2_FIELD_INTERLACED) ? ~0 : 0; | ||
400 | vp_write_mask(mdev, VP_MODE, val, | ||
401 | VP_MODE_LINE_SKIP | VP_MODE_FIELD_ID_AUTO_TOGGLING); | ||
402 | |||
403 | mxr_vsync_set_update(mdev, MXR_ENABLE); | ||
404 | spin_unlock_irqrestore(&mdev->reg_slock, flags); | ||
405 | } | ||
406 | |||
407 | void mxr_reg_graph_layer_stream(struct mxr_device *mdev, int idx, int en) | ||
408 | { | ||
409 | /* no extra actions need to be done */ | ||
410 | } | ||
411 | |||
412 | void mxr_reg_vp_layer_stream(struct mxr_device *mdev, int en) | ||
413 | { | ||
414 | /* no extra actions need to be done */ | ||
415 | } | ||
416 | |||
417 | static const u8 filter_y_horiz_tap8[] = { | ||
418 | 0, -1, -1, -1, -1, -1, -1, -1, | ||
419 | -1, -1, -1, -1, -1, 0, 0, 0, | ||
420 | 0, 2, 4, 5, 6, 6, 6, 6, | ||
421 | 6, 5, 5, 4, 3, 2, 1, 1, | ||
422 | 0, -6, -12, -16, -18, -20, -21, -20, | ||
423 | -20, -18, -16, -13, -10, -8, -5, -2, | ||
424 | 127, 126, 125, 121, 114, 107, 99, 89, | ||
425 | 79, 68, 57, 46, 35, 25, 16, 8, | ||
426 | }; | ||
427 | |||
428 | static const u8 filter_y_vert_tap4[] = { | ||
429 | 0, -3, -6, -8, -8, -8, -8, -7, | ||
430 | -6, -5, -4, -3, -2, -1, -1, 0, | ||
431 | 127, 126, 124, 118, 111, 102, 92, 81, | ||
432 | 70, 59, 48, 37, 27, 19, 11, 5, | ||
433 | 0, 5, 11, 19, 27, 37, 48, 59, | ||
434 | 70, 81, 92, 102, 111, 118, 124, 126, | ||
435 | 0, 0, -1, -1, -2, -3, -4, -5, | ||
436 | -6, -7, -8, -8, -8, -8, -6, -3, | ||
437 | }; | ||
438 | |||
439 | static const u8 filter_cr_horiz_tap4[] = { | ||
440 | 0, -3, -6, -8, -8, -8, -8, -7, | ||
441 | -6, -5, -4, -3, -2, -1, -1, 0, | ||
442 | 127, 126, 124, 118, 111, 102, 92, 81, | ||
443 | 70, 59, 48, 37, 27, 19, 11, 5, | ||
444 | }; | ||
445 | |||
446 | static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, | ||
447 | int reg_id, const u8 *data, unsigned int size) | ||
448 | { | ||
449 | /* assure 4-byte align */ | ||
450 | BUG_ON(size & 3); | ||
451 | for (; size; size -= 4, reg_id += 4, data += 4) { | ||
452 | u32 val = (data[0] << 24) | (data[1] << 16) | | ||
453 | (data[2] << 8) | data[3]; | ||
454 | vp_write(mdev, reg_id, val); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | static void mxr_reg_vp_default_filter(struct mxr_device *mdev) | ||
459 | { | ||
460 | mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL, | ||
461 | filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); | ||
462 | mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, | ||
463 | filter_y_vert_tap4, sizeof filter_y_vert_tap4); | ||
464 | mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL, | ||
465 | filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); | ||
466 | } | ||
467 | |||
468 | static void mxr_reg_mxr_dump(struct mxr_device *mdev) | ||
469 | { | ||
470 | #define DUMPREG(reg_id) \ | ||
471 | do { \ | ||
472 | mxr_dbg(mdev, #reg_id " = %08x\n", \ | ||
473 | (u32)readl(mdev->res.mxr_regs + reg_id)); \ | ||
474 | } while (0) | ||
475 | |||
476 | DUMPREG(MXR_STATUS); | ||
477 | DUMPREG(MXR_CFG); | ||
478 | DUMPREG(MXR_INT_EN); | ||
479 | DUMPREG(MXR_INT_STATUS); | ||
480 | |||
481 | DUMPREG(MXR_LAYER_CFG); | ||
482 | DUMPREG(MXR_VIDEO_CFG); | ||
483 | |||
484 | DUMPREG(MXR_GRAPHIC0_CFG); | ||
485 | DUMPREG(MXR_GRAPHIC0_BASE); | ||
486 | DUMPREG(MXR_GRAPHIC0_SPAN); | ||
487 | DUMPREG(MXR_GRAPHIC0_WH); | ||
488 | DUMPREG(MXR_GRAPHIC0_SXY); | ||
489 | DUMPREG(MXR_GRAPHIC0_DXY); | ||
490 | |||
491 | DUMPREG(MXR_GRAPHIC1_CFG); | ||
492 | DUMPREG(MXR_GRAPHIC1_BASE); | ||
493 | DUMPREG(MXR_GRAPHIC1_SPAN); | ||
494 | DUMPREG(MXR_GRAPHIC1_WH); | ||
495 | DUMPREG(MXR_GRAPHIC1_SXY); | ||
496 | DUMPREG(MXR_GRAPHIC1_DXY); | ||
497 | #undef DUMPREG | ||
498 | } | ||
499 | |||
500 | static void mxr_reg_vp_dump(struct mxr_device *mdev) | ||
501 | { | ||
502 | #define DUMPREG(reg_id) \ | ||
503 | do { \ | ||
504 | mxr_dbg(mdev, #reg_id " = %08x\n", \ | ||
505 | (u32) readl(mdev->res.vp_regs + reg_id)); \ | ||
506 | } while (0) | ||
507 | |||
508 | |||
509 | DUMPREG(VP_ENABLE); | ||
510 | DUMPREG(VP_SRESET); | ||
511 | DUMPREG(VP_SHADOW_UPDATE); | ||
512 | DUMPREG(VP_FIELD_ID); | ||
513 | DUMPREG(VP_MODE); | ||
514 | DUMPREG(VP_IMG_SIZE_Y); | ||
515 | DUMPREG(VP_IMG_SIZE_C); | ||
516 | DUMPREG(VP_PER_RATE_CTRL); | ||
517 | DUMPREG(VP_TOP_Y_PTR); | ||
518 | DUMPREG(VP_BOT_Y_PTR); | ||
519 | DUMPREG(VP_TOP_C_PTR); | ||
520 | DUMPREG(VP_BOT_C_PTR); | ||
521 | DUMPREG(VP_ENDIAN_MODE); | ||
522 | DUMPREG(VP_SRC_H_POSITION); | ||
523 | DUMPREG(VP_SRC_V_POSITION); | ||
524 | DUMPREG(VP_SRC_WIDTH); | ||
525 | DUMPREG(VP_SRC_HEIGHT); | ||
526 | DUMPREG(VP_DST_H_POSITION); | ||
527 | DUMPREG(VP_DST_V_POSITION); | ||
528 | DUMPREG(VP_DST_WIDTH); | ||
529 | DUMPREG(VP_DST_HEIGHT); | ||
530 | DUMPREG(VP_H_RATIO); | ||
531 | DUMPREG(VP_V_RATIO); | ||
532 | |||
533 | #undef DUMPREG | ||
534 | } | ||
535 | |||
536 | void mxr_reg_dump(struct mxr_device *mdev) | ||
537 | { | ||
538 | mxr_reg_mxr_dump(mdev); | ||
539 | mxr_reg_vp_dump(mdev); | ||
540 | } | ||
541 | |||
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c new file mode 100644 index 00000000000..43ac22f35bc --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer_video.c | |||
@@ -0,0 +1,1006 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include "mixer.h" | ||
15 | |||
16 | #include <media/v4l2-ioctl.h> | ||
17 | #include <linux/videodev2.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/version.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <media/videobuf2-dma-contig.h> | ||
22 | |||
23 | static int find_reg_callback(struct device *dev, void *p) | ||
24 | { | ||
25 | struct v4l2_subdev **sd = p; | ||
26 | |||
27 | *sd = dev_get_drvdata(dev); | ||
28 | /* non-zero value stops iteration */ | ||
29 | return 1; | ||
30 | } | ||
31 | |||
32 | static struct v4l2_subdev *find_and_register_subdev( | ||
33 | struct mxr_device *mdev, char *module_name) | ||
34 | { | ||
35 | struct device_driver *drv; | ||
36 | struct v4l2_subdev *sd = NULL; | ||
37 | int ret; | ||
38 | |||
39 | /* TODO: add waiting until probe is finished */ | ||
40 | drv = driver_find(module_name, &platform_bus_type); | ||
41 | if (!drv) { | ||
42 | mxr_warn(mdev, "module %s is missing\n", module_name); | ||
43 | return NULL; | ||
44 | } | ||
45 | /* driver refcnt is increased, it is safe to iterate over devices */ | ||
46 | ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback); | ||
47 | /* ret == 0 means that find_reg_callback was never executed */ | ||
48 | if (sd == NULL) { | ||
49 | mxr_warn(mdev, "module %s provides no subdev!\n", module_name); | ||
50 | goto done; | ||
51 | } | ||
52 | /* v4l2_device_register_subdev detects if sd is NULL */ | ||
53 | ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd); | ||
54 | if (ret) { | ||
55 | mxr_warn(mdev, "failed to register subdev %s\n", sd->name); | ||
56 | sd = NULL; | ||
57 | } | ||
58 | |||
59 | done: | ||
60 | put_driver(drv); | ||
61 | return sd; | ||
62 | } | ||
63 | |||
64 | int __devinit mxr_acquire_video(struct mxr_device *mdev, | ||
65 | struct mxr_output_conf *output_conf, int output_count) | ||
66 | { | ||
67 | struct device *dev = mdev->dev; | ||
68 | struct v4l2_device *v4l2_dev = &mdev->v4l2_dev; | ||
69 | int i; | ||
70 | int ret = 0; | ||
71 | struct v4l2_subdev *sd; | ||
72 | |||
73 | strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name)); | ||
74 | /* prepare context for V4L2 device */ | ||
75 | ret = v4l2_device_register(dev, v4l2_dev); | ||
76 | if (ret) { | ||
77 | mxr_err(mdev, "could not register v4l2 device.\n"); | ||
78 | goto fail; | ||
79 | } | ||
80 | |||
81 | mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); | ||
82 | if (IS_ERR_OR_NULL(mdev->alloc_ctx)) { | ||
83 | mxr_err(mdev, "could not acquire vb2 allocator\n"); | ||
84 | goto fail_v4l2_dev; | ||
85 | } | ||
86 | |||
87 | /* registering outputs */ | ||
88 | mdev->output_cnt = 0; | ||
89 | for (i = 0; i < output_count; ++i) { | ||
90 | struct mxr_output_conf *conf = &output_conf[i]; | ||
91 | struct mxr_output *out; | ||
92 | |||
93 | sd = find_and_register_subdev(mdev, conf->module_name); | ||
94 | /* trying to register next output */ | ||
95 | if (sd == NULL) | ||
96 | continue; | ||
97 | out = kzalloc(sizeof *out, GFP_KERNEL); | ||
98 | if (out == NULL) { | ||
99 | mxr_err(mdev, "no memory for '%s'\n", | ||
100 | conf->output_name); | ||
101 | ret = -ENOMEM; | ||
102 | /* registered subdevs are removed in fail_v4l2_dev */ | ||
103 | goto fail_output; | ||
104 | } | ||
105 | strlcpy(out->name, conf->output_name, sizeof(out->name)); | ||
106 | out->sd = sd; | ||
107 | out->cookie = conf->cookie; | ||
108 | mdev->output[mdev->output_cnt++] = out; | ||
109 | mxr_info(mdev, "added output '%s' from module '%s'\n", | ||
110 | conf->output_name, conf->module_name); | ||
111 | /* checking if maximal number of outputs is reached */ | ||
112 | if (mdev->output_cnt >= MXR_MAX_OUTPUTS) | ||
113 | break; | ||
114 | } | ||
115 | |||
116 | if (mdev->output_cnt == 0) { | ||
117 | mxr_err(mdev, "failed to register any output\n"); | ||
118 | ret = -ENODEV; | ||
119 | /* skipping fail_output because there is nothing to free */ | ||
120 | goto fail_vb2_allocator; | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | |||
125 | fail_output: | ||
126 | /* kfree is NULL-safe */ | ||
127 | for (i = 0; i < mdev->output_cnt; ++i) | ||
128 | kfree(mdev->output[i]); | ||
129 | memset(mdev->output, 0, sizeof mdev->output); | ||
130 | |||
131 | fail_vb2_allocator: | ||
132 | /* freeing allocator context */ | ||
133 | vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); | ||
134 | |||
135 | fail_v4l2_dev: | ||
136 | /* NOTE: automatically unregister all subdevs */ | ||
137 | v4l2_device_unregister(v4l2_dev); | ||
138 | |||
139 | fail: | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | void __devexit mxr_release_video(struct mxr_device *mdev) | ||
144 | { | ||
145 | int i; | ||
146 | |||
147 | /* kfree is NULL-safe */ | ||
148 | for (i = 0; i < mdev->output_cnt; ++i) | ||
149 | kfree(mdev->output[i]); | ||
150 | |||
151 | vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx); | ||
152 | v4l2_device_unregister(&mdev->v4l2_dev); | ||
153 | } | ||
154 | |||
155 | static int mxr_querycap(struct file *file, void *priv, | ||
156 | struct v4l2_capability *cap) | ||
157 | { | ||
158 | struct mxr_layer *layer = video_drvdata(file); | ||
159 | |||
160 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
161 | |||
162 | strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); | ||
163 | strlcpy(cap->card, layer->vfd.name, sizeof cap->card); | ||
164 | sprintf(cap->bus_info, "%d", layer->idx); | ||
165 | cap->version = KERNEL_VERSION(0, 1, 0); | ||
166 | cap->capabilities = V4L2_CAP_STREAMING | | ||
167 | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | /* Geometry handling */ | ||
173 | static void mxr_layer_geo_fix(struct mxr_layer *layer) | ||
174 | { | ||
175 | struct mxr_device *mdev = layer->mdev; | ||
176 | struct v4l2_mbus_framefmt mbus_fmt; | ||
177 | |||
178 | /* TODO: add some dirty flag to avoid unnecessary adjustments */ | ||
179 | mxr_get_mbus_fmt(mdev, &mbus_fmt); | ||
180 | layer->geo.dst.full_width = mbus_fmt.width; | ||
181 | layer->geo.dst.full_height = mbus_fmt.height; | ||
182 | layer->geo.dst.field = mbus_fmt.field; | ||
183 | layer->ops.fix_geometry(layer); | ||
184 | } | ||
185 | |||
186 | static void mxr_layer_default_geo(struct mxr_layer *layer) | ||
187 | { | ||
188 | struct mxr_device *mdev = layer->mdev; | ||
189 | struct v4l2_mbus_framefmt mbus_fmt; | ||
190 | |||
191 | memset(&layer->geo, 0, sizeof layer->geo); | ||
192 | |||
193 | mxr_get_mbus_fmt(mdev, &mbus_fmt); | ||
194 | |||
195 | layer->geo.dst.full_width = mbus_fmt.width; | ||
196 | layer->geo.dst.full_height = mbus_fmt.height; | ||
197 | layer->geo.dst.width = layer->geo.dst.full_width; | ||
198 | layer->geo.dst.height = layer->geo.dst.full_height; | ||
199 | layer->geo.dst.field = mbus_fmt.field; | ||
200 | |||
201 | layer->geo.src.full_width = mbus_fmt.width; | ||
202 | layer->geo.src.full_height = mbus_fmt.height; | ||
203 | layer->geo.src.width = layer->geo.src.full_width; | ||
204 | layer->geo.src.height = layer->geo.src.full_height; | ||
205 | |||
206 | layer->ops.fix_geometry(layer); | ||
207 | } | ||
208 | |||
209 | static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo) | ||
210 | { | ||
211 | mxr_dbg(mdev, "src.full_size = (%u, %u)\n", | ||
212 | geo->src.full_width, geo->src.full_height); | ||
213 | mxr_dbg(mdev, "src.size = (%u, %u)\n", | ||
214 | geo->src.width, geo->src.height); | ||
215 | mxr_dbg(mdev, "src.offset = (%u, %u)\n", | ||
216 | geo->src.x_offset, geo->src.y_offset); | ||
217 | mxr_dbg(mdev, "dst.full_size = (%u, %u)\n", | ||
218 | geo->dst.full_width, geo->dst.full_height); | ||
219 | mxr_dbg(mdev, "dst.size = (%u, %u)\n", | ||
220 | geo->dst.width, geo->dst.height); | ||
221 | mxr_dbg(mdev, "dst.offset = (%u, %u)\n", | ||
222 | geo->dst.x_offset, geo->dst.y_offset); | ||
223 | mxr_dbg(mdev, "ratio = (%u, %u)\n", | ||
224 | geo->x_ratio, geo->y_ratio); | ||
225 | } | ||
226 | |||
227 | |||
228 | static const struct mxr_format *find_format_by_fourcc( | ||
229 | struct mxr_layer *layer, unsigned long fourcc); | ||
230 | static const struct mxr_format *find_format_by_index( | ||
231 | struct mxr_layer *layer, unsigned long index); | ||
232 | |||
233 | static int mxr_enum_fmt(struct file *file, void *priv, | ||
234 | struct v4l2_fmtdesc *f) | ||
235 | { | ||
236 | struct mxr_layer *layer = video_drvdata(file); | ||
237 | struct mxr_device *mdev = layer->mdev; | ||
238 | const struct mxr_format *fmt; | ||
239 | |||
240 | mxr_dbg(mdev, "%s\n", __func__); | ||
241 | fmt = find_format_by_index(layer, f->index); | ||
242 | if (fmt == NULL) | ||
243 | return -EINVAL; | ||
244 | |||
245 | strlcpy(f->description, fmt->name, sizeof(f->description)); | ||
246 | f->pixelformat = fmt->fourcc; | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | static int mxr_s_fmt(struct file *file, void *priv, | ||
252 | struct v4l2_format *f) | ||
253 | { | ||
254 | struct mxr_layer *layer = video_drvdata(file); | ||
255 | const struct mxr_format *fmt; | ||
256 | struct v4l2_pix_format_mplane *pix; | ||
257 | struct mxr_device *mdev = layer->mdev; | ||
258 | struct mxr_geometry *geo = &layer->geo; | ||
259 | |||
260 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); | ||
261 | |||
262 | pix = &f->fmt.pix_mp; | ||
263 | fmt = find_format_by_fourcc(layer, pix->pixelformat); | ||
264 | if (fmt == NULL) { | ||
265 | mxr_warn(mdev, "not recognized fourcc: %08x\n", | ||
266 | pix->pixelformat); | ||
267 | return -EINVAL; | ||
268 | } | ||
269 | layer->fmt = fmt; | ||
270 | geo->src.full_width = pix->width; | ||
271 | geo->src.width = pix->width; | ||
272 | geo->src.full_height = pix->height; | ||
273 | geo->src.height = pix->height; | ||
274 | /* assure consistency of geometry */ | ||
275 | mxr_layer_geo_fix(layer); | ||
276 | mxr_dbg(mdev, "width=%u height=%u span=%u\n", | ||
277 | geo->src.width, geo->src.height, geo->src.full_width); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static unsigned int divup(unsigned int divident, unsigned int divisor) | ||
283 | { | ||
284 | return (divident + divisor - 1) / divisor; | ||
285 | } | ||
286 | |||
287 | unsigned long mxr_get_plane_size(const struct mxr_block *blk, | ||
288 | unsigned int width, unsigned int height) | ||
289 | { | ||
290 | unsigned int bl_width = divup(width, blk->width); | ||
291 | unsigned int bl_height = divup(height, blk->height); | ||
292 | |||
293 | return bl_width * bl_height * blk->size; | ||
294 | } | ||
295 | |||
296 | static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes, | ||
297 | const struct mxr_format *fmt, u32 width, u32 height) | ||
298 | { | ||
299 | int i; | ||
300 | |||
301 | memset(planes, 0, sizeof(*planes) * fmt->num_subframes); | ||
302 | for (i = 0; i < fmt->num_planes; ++i) { | ||
303 | struct v4l2_plane_pix_format *plane = planes | ||
304 | + fmt->plane2subframe[i]; | ||
305 | const struct mxr_block *blk = &fmt->plane[i]; | ||
306 | u32 bl_width = divup(width, blk->width); | ||
307 | u32 bl_height = divup(height, blk->height); | ||
308 | u32 sizeimage = bl_width * bl_height * blk->size; | ||
309 | u16 bytesperline = bl_width * blk->size / blk->height; | ||
310 | |||
311 | plane->sizeimage += sizeimage; | ||
312 | plane->bytesperline = max(plane->bytesperline, bytesperline); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | static int mxr_g_fmt(struct file *file, void *priv, | ||
317 | struct v4l2_format *f) | ||
318 | { | ||
319 | struct mxr_layer *layer = video_drvdata(file); | ||
320 | struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; | ||
321 | |||
322 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
323 | |||
324 | pix->width = layer->geo.src.full_width; | ||
325 | pix->height = layer->geo.src.full_height; | ||
326 | pix->field = V4L2_FIELD_NONE; | ||
327 | pix->pixelformat = layer->fmt->fourcc; | ||
328 | pix->colorspace = layer->fmt->colorspace; | ||
329 | mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height); | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static inline struct mxr_crop *choose_crop_by_type(struct mxr_geometry *geo, | ||
335 | enum v4l2_buf_type type) | ||
336 | { | ||
337 | switch (type) { | ||
338 | case V4L2_BUF_TYPE_VIDEO_OUTPUT: | ||
339 | case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: | ||
340 | return &geo->dst; | ||
341 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | ||
342 | return &geo->src; | ||
343 | default: | ||
344 | return NULL; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | static int mxr_g_crop(struct file *file, void *fh, struct v4l2_crop *a) | ||
349 | { | ||
350 | struct mxr_layer *layer = video_drvdata(file); | ||
351 | struct mxr_crop *crop; | ||
352 | |||
353 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
354 | crop = choose_crop_by_type(&layer->geo, a->type); | ||
355 | if (crop == NULL) | ||
356 | return -EINVAL; | ||
357 | mxr_layer_geo_fix(layer); | ||
358 | a->c.left = crop->x_offset; | ||
359 | a->c.top = crop->y_offset; | ||
360 | a->c.width = crop->width; | ||
361 | a->c.height = crop->height; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int mxr_s_crop(struct file *file, void *fh, struct v4l2_crop *a) | ||
366 | { | ||
367 | struct mxr_layer *layer = video_drvdata(file); | ||
368 | struct mxr_crop *crop; | ||
369 | |||
370 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
371 | crop = choose_crop_by_type(&layer->geo, a->type); | ||
372 | if (crop == NULL) | ||
373 | return -EINVAL; | ||
374 | crop->x_offset = a->c.left; | ||
375 | crop->y_offset = a->c.top; | ||
376 | crop->width = a->c.width; | ||
377 | crop->height = a->c.height; | ||
378 | mxr_layer_geo_fix(layer); | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int mxr_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) | ||
383 | { | ||
384 | struct mxr_layer *layer = video_drvdata(file); | ||
385 | struct mxr_crop *crop; | ||
386 | |||
387 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
388 | crop = choose_crop_by_type(&layer->geo, a->type); | ||
389 | if (crop == NULL) | ||
390 | return -EINVAL; | ||
391 | mxr_layer_geo_fix(layer); | ||
392 | a->bounds.left = 0; | ||
393 | a->bounds.top = 0; | ||
394 | a->bounds.width = crop->full_width; | ||
395 | a->bounds.top = crop->full_height; | ||
396 | a->defrect = a->bounds; | ||
397 | /* setting pixel aspect to 1/1 */ | ||
398 | a->pixelaspect.numerator = 1; | ||
399 | a->pixelaspect.denominator = 1; | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static int mxr_enum_dv_presets(struct file *file, void *fh, | ||
404 | struct v4l2_dv_enum_preset *preset) | ||
405 | { | ||
406 | struct mxr_layer *layer = video_drvdata(file); | ||
407 | struct mxr_device *mdev = layer->mdev; | ||
408 | int ret; | ||
409 | |||
410 | /* lock protects from changing sd_out */ | ||
411 | mutex_lock(&mdev->mutex); | ||
412 | ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset); | ||
413 | mutex_unlock(&mdev->mutex); | ||
414 | |||
415 | return ret ? -EINVAL : 0; | ||
416 | } | ||
417 | |||
418 | static int mxr_s_dv_preset(struct file *file, void *fh, | ||
419 | struct v4l2_dv_preset *preset) | ||
420 | { | ||
421 | struct mxr_layer *layer = video_drvdata(file); | ||
422 | struct mxr_device *mdev = layer->mdev; | ||
423 | int ret; | ||
424 | |||
425 | /* lock protects from changing sd_out */ | ||
426 | mutex_lock(&mdev->mutex); | ||
427 | |||
428 | /* preset change cannot be done while there is an entity | ||
429 | * dependant on output configuration | ||
430 | */ | ||
431 | if (mdev->n_output > 0) { | ||
432 | mutex_unlock(&mdev->mutex); | ||
433 | return -EBUSY; | ||
434 | } | ||
435 | |||
436 | ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset); | ||
437 | |||
438 | mutex_unlock(&mdev->mutex); | ||
439 | |||
440 | /* any failure should return EINVAL according to V4L2 doc */ | ||
441 | return ret ? -EINVAL : 0; | ||
442 | } | ||
443 | |||
444 | static int mxr_g_dv_preset(struct file *file, void *fh, | ||
445 | struct v4l2_dv_preset *preset) | ||
446 | { | ||
447 | struct mxr_layer *layer = video_drvdata(file); | ||
448 | struct mxr_device *mdev = layer->mdev; | ||
449 | int ret; | ||
450 | |||
451 | /* lock protects from changing sd_out */ | ||
452 | mutex_lock(&mdev->mutex); | ||
453 | ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset); | ||
454 | mutex_unlock(&mdev->mutex); | ||
455 | |||
456 | return ret ? -EINVAL : 0; | ||
457 | } | ||
458 | |||
459 | static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm) | ||
460 | { | ||
461 | struct mxr_layer *layer = video_drvdata(file); | ||
462 | struct mxr_device *mdev = layer->mdev; | ||
463 | int ret; | ||
464 | |||
465 | /* lock protects from changing sd_out */ | ||
466 | mutex_lock(&mdev->mutex); | ||
467 | |||
468 | /* standard change cannot be done while there is an entity | ||
469 | * dependant on output configuration | ||
470 | */ | ||
471 | if (mdev->n_output > 0) { | ||
472 | mutex_unlock(&mdev->mutex); | ||
473 | return -EBUSY; | ||
474 | } | ||
475 | |||
476 | ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm); | ||
477 | |||
478 | mutex_unlock(&mdev->mutex); | ||
479 | |||
480 | return ret ? -EINVAL : 0; | ||
481 | } | ||
482 | |||
483 | static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm) | ||
484 | { | ||
485 | struct mxr_layer *layer = video_drvdata(file); | ||
486 | struct mxr_device *mdev = layer->mdev; | ||
487 | int ret; | ||
488 | |||
489 | /* lock protects from changing sd_out */ | ||
490 | mutex_lock(&mdev->mutex); | ||
491 | ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm); | ||
492 | mutex_unlock(&mdev->mutex); | ||
493 | |||
494 | return ret ? -EINVAL : 0; | ||
495 | } | ||
496 | |||
497 | static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a) | ||
498 | { | ||
499 | struct mxr_layer *layer = video_drvdata(file); | ||
500 | struct mxr_device *mdev = layer->mdev; | ||
501 | struct mxr_output *out; | ||
502 | struct v4l2_subdev *sd; | ||
503 | |||
504 | if (a->index >= mdev->output_cnt) | ||
505 | return -EINVAL; | ||
506 | out = mdev->output[a->index]; | ||
507 | BUG_ON(out == NULL); | ||
508 | sd = out->sd; | ||
509 | strlcpy(a->name, out->name, sizeof(a->name)); | ||
510 | |||
511 | /* try to obtain supported tv norms */ | ||
512 | v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std); | ||
513 | a->capabilities = 0; | ||
514 | if (sd->ops->video && sd->ops->video->s_dv_preset) | ||
515 | a->capabilities |= V4L2_OUT_CAP_PRESETS; | ||
516 | if (sd->ops->video && sd->ops->video->s_std_output) | ||
517 | a->capabilities |= V4L2_OUT_CAP_STD; | ||
518 | a->type = V4L2_OUTPUT_TYPE_ANALOG; | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | static int mxr_s_output(struct file *file, void *fh, unsigned int i) | ||
524 | { | ||
525 | struct video_device *vfd = video_devdata(file); | ||
526 | struct mxr_layer *layer = video_drvdata(file); | ||
527 | struct mxr_device *mdev = layer->mdev; | ||
528 | int ret = 0; | ||
529 | |||
530 | if (i >= mdev->output_cnt || mdev->output[i] == NULL) | ||
531 | return -EINVAL; | ||
532 | |||
533 | mutex_lock(&mdev->mutex); | ||
534 | if (mdev->n_output > 0) { | ||
535 | ret = -EBUSY; | ||
536 | goto done; | ||
537 | } | ||
538 | mdev->current_output = i; | ||
539 | vfd->tvnorms = 0; | ||
540 | v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output, | ||
541 | &vfd->tvnorms); | ||
542 | mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms); | ||
543 | |||
544 | done: | ||
545 | mutex_unlock(&mdev->mutex); | ||
546 | return ret; | ||
547 | } | ||
548 | |||
549 | static int mxr_g_output(struct file *file, void *fh, unsigned int *p) | ||
550 | { | ||
551 | struct mxr_layer *layer = video_drvdata(file); | ||
552 | struct mxr_device *mdev = layer->mdev; | ||
553 | |||
554 | mutex_lock(&mdev->mutex); | ||
555 | *p = mdev->current_output; | ||
556 | mutex_unlock(&mdev->mutex); | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static int mxr_reqbufs(struct file *file, void *priv, | ||
562 | struct v4l2_requestbuffers *p) | ||
563 | { | ||
564 | struct mxr_layer *layer = video_drvdata(file); | ||
565 | |||
566 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
567 | return vb2_reqbufs(&layer->vb_queue, p); | ||
568 | } | ||
569 | |||
570 | static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
571 | { | ||
572 | struct mxr_layer *layer = video_drvdata(file); | ||
573 | |||
574 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
575 | return vb2_querybuf(&layer->vb_queue, p); | ||
576 | } | ||
577 | |||
578 | static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
579 | { | ||
580 | struct mxr_layer *layer = video_drvdata(file); | ||
581 | |||
582 | mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index); | ||
583 | return vb2_qbuf(&layer->vb_queue, p); | ||
584 | } | ||
585 | |||
586 | static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) | ||
587 | { | ||
588 | struct mxr_layer *layer = video_drvdata(file); | ||
589 | |||
590 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
591 | return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK); | ||
592 | } | ||
593 | |||
594 | static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i) | ||
595 | { | ||
596 | struct mxr_layer *layer = video_drvdata(file); | ||
597 | |||
598 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
599 | return vb2_streamon(&layer->vb_queue, i); | ||
600 | } | ||
601 | |||
602 | static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) | ||
603 | { | ||
604 | struct mxr_layer *layer = video_drvdata(file); | ||
605 | |||
606 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
607 | return vb2_streamoff(&layer->vb_queue, i); | ||
608 | } | ||
609 | |||
610 | static const struct v4l2_ioctl_ops mxr_ioctl_ops = { | ||
611 | .vidioc_querycap = mxr_querycap, | ||
612 | /* format handling */ | ||
613 | .vidioc_enum_fmt_vid_out = mxr_enum_fmt, | ||
614 | .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt, | ||
615 | .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt, | ||
616 | /* buffer control */ | ||
617 | .vidioc_reqbufs = mxr_reqbufs, | ||
618 | .vidioc_querybuf = mxr_querybuf, | ||
619 | .vidioc_qbuf = mxr_qbuf, | ||
620 | .vidioc_dqbuf = mxr_dqbuf, | ||
621 | /* Streaming control */ | ||
622 | .vidioc_streamon = mxr_streamon, | ||
623 | .vidioc_streamoff = mxr_streamoff, | ||
624 | /* Preset functions */ | ||
625 | .vidioc_enum_dv_presets = mxr_enum_dv_presets, | ||
626 | .vidioc_s_dv_preset = mxr_s_dv_preset, | ||
627 | .vidioc_g_dv_preset = mxr_g_dv_preset, | ||
628 | /* analog TV standard functions */ | ||
629 | .vidioc_s_std = mxr_s_std, | ||
630 | .vidioc_g_std = mxr_g_std, | ||
631 | /* Output handling */ | ||
632 | .vidioc_enum_output = mxr_enum_output, | ||
633 | .vidioc_s_output = mxr_s_output, | ||
634 | .vidioc_g_output = mxr_g_output, | ||
635 | /* Crop ioctls */ | ||
636 | .vidioc_g_crop = mxr_g_crop, | ||
637 | .vidioc_s_crop = mxr_s_crop, | ||
638 | .vidioc_cropcap = mxr_cropcap, | ||
639 | }; | ||
640 | |||
641 | static int mxr_video_open(struct file *file) | ||
642 | { | ||
643 | struct mxr_layer *layer = video_drvdata(file); | ||
644 | struct mxr_device *mdev = layer->mdev; | ||
645 | int ret = 0; | ||
646 | |||
647 | mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__); | ||
648 | /* assure device probe is finished */ | ||
649 | wait_for_device_probe(); | ||
650 | /* creating context for file descriptor */ | ||
651 | ret = v4l2_fh_open(file); | ||
652 | if (ret) { | ||
653 | mxr_err(mdev, "v4l2_fh_open failed\n"); | ||
654 | return ret; | ||
655 | } | ||
656 | |||
657 | /* leaving if layer is already initialized */ | ||
658 | if (!v4l2_fh_is_singular_file(file)) | ||
659 | return 0; | ||
660 | |||
661 | /* FIXME: should power be enabled on open? */ | ||
662 | ret = mxr_power_get(mdev); | ||
663 | if (ret) { | ||
664 | mxr_err(mdev, "power on failed\n"); | ||
665 | goto fail_fh_open; | ||
666 | } | ||
667 | |||
668 | ret = vb2_queue_init(&layer->vb_queue); | ||
669 | if (ret != 0) { | ||
670 | mxr_err(mdev, "failed to initialize vb2 queue\n"); | ||
671 | goto fail_power; | ||
672 | } | ||
673 | /* set default format, first on the list */ | ||
674 | layer->fmt = layer->fmt_array[0]; | ||
675 | /* setup default geometry */ | ||
676 | mxr_layer_default_geo(layer); | ||
677 | |||
678 | return 0; | ||
679 | |||
680 | fail_power: | ||
681 | mxr_power_put(mdev); | ||
682 | |||
683 | fail_fh_open: | ||
684 | v4l2_fh_release(file); | ||
685 | |||
686 | return ret; | ||
687 | } | ||
688 | |||
689 | static unsigned int | ||
690 | mxr_video_poll(struct file *file, struct poll_table_struct *wait) | ||
691 | { | ||
692 | struct mxr_layer *layer = video_drvdata(file); | ||
693 | |||
694 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
695 | |||
696 | return vb2_poll(&layer->vb_queue, file, wait); | ||
697 | } | ||
698 | |||
699 | static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma) | ||
700 | { | ||
701 | struct mxr_layer *layer = video_drvdata(file); | ||
702 | |||
703 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
704 | |||
705 | return vb2_mmap(&layer->vb_queue, vma); | ||
706 | } | ||
707 | |||
708 | static int mxr_video_release(struct file *file) | ||
709 | { | ||
710 | struct mxr_layer *layer = video_drvdata(file); | ||
711 | |||
712 | mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); | ||
713 | if (v4l2_fh_is_singular_file(file)) { | ||
714 | vb2_queue_release(&layer->vb_queue); | ||
715 | mxr_power_put(layer->mdev); | ||
716 | } | ||
717 | v4l2_fh_release(file); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | static const struct v4l2_file_operations mxr_fops = { | ||
722 | .owner = THIS_MODULE, | ||
723 | .open = mxr_video_open, | ||
724 | .poll = mxr_video_poll, | ||
725 | .mmap = mxr_video_mmap, | ||
726 | .release = mxr_video_release, | ||
727 | .unlocked_ioctl = video_ioctl2, | ||
728 | }; | ||
729 | |||
730 | static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, | ||
731 | unsigned int *nplanes, unsigned long sizes[], | ||
732 | void *alloc_ctxs[]) | ||
733 | { | ||
734 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | ||
735 | const struct mxr_format *fmt = layer->fmt; | ||
736 | int i; | ||
737 | struct mxr_device *mdev = layer->mdev; | ||
738 | struct v4l2_plane_pix_format planes[3]; | ||
739 | |||
740 | mxr_dbg(mdev, "%s\n", __func__); | ||
741 | /* checking if format was configured */ | ||
742 | if (fmt == NULL) | ||
743 | return -EINVAL; | ||
744 | mxr_dbg(mdev, "fmt = %s\n", fmt->name); | ||
745 | mxr_mplane_fill(planes, fmt, layer->geo.src.full_width, | ||
746 | layer->geo.src.full_height); | ||
747 | |||
748 | *nplanes = fmt->num_subframes; | ||
749 | for (i = 0; i < fmt->num_subframes; ++i) { | ||
750 | alloc_ctxs[i] = layer->mdev->alloc_ctx; | ||
751 | sizes[i] = PAGE_ALIGN(planes[i].sizeimage); | ||
752 | mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]); | ||
753 | } | ||
754 | |||
755 | if (*nbuffers == 0) | ||
756 | *nbuffers = 1; | ||
757 | |||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static void buf_queue(struct vb2_buffer *vb) | ||
762 | { | ||
763 | struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb); | ||
764 | struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue); | ||
765 | struct mxr_device *mdev = layer->mdev; | ||
766 | unsigned long flags; | ||
767 | int must_start = 0; | ||
768 | |||
769 | spin_lock_irqsave(&layer->enq_slock, flags); | ||
770 | if (layer->state == MXR_LAYER_STREAMING_START) { | ||
771 | layer->state = MXR_LAYER_STREAMING; | ||
772 | must_start = 1; | ||
773 | } | ||
774 | list_add_tail(&buffer->list, &layer->enq_list); | ||
775 | spin_unlock_irqrestore(&layer->enq_slock, flags); | ||
776 | if (must_start) { | ||
777 | layer->ops.stream_set(layer, MXR_ENABLE); | ||
778 | mxr_streamer_get(mdev); | ||
779 | } | ||
780 | |||
781 | mxr_dbg(mdev, "queuing buffer\n"); | ||
782 | } | ||
783 | |||
784 | static void wait_lock(struct vb2_queue *vq) | ||
785 | { | ||
786 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | ||
787 | |||
788 | mxr_dbg(layer->mdev, "%s\n", __func__); | ||
789 | mutex_lock(&layer->mutex); | ||
790 | } | ||
791 | |||
792 | static void wait_unlock(struct vb2_queue *vq) | ||
793 | { | ||
794 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | ||
795 | |||
796 | mxr_dbg(layer->mdev, "%s\n", __func__); | ||
797 | mutex_unlock(&layer->mutex); | ||
798 | } | ||
799 | |||
800 | static int start_streaming(struct vb2_queue *vq) | ||
801 | { | ||
802 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | ||
803 | struct mxr_device *mdev = layer->mdev; | ||
804 | unsigned long flags; | ||
805 | |||
806 | mxr_dbg(mdev, "%s\n", __func__); | ||
807 | /* block any changes in output configuration */ | ||
808 | mxr_output_get(mdev); | ||
809 | |||
810 | /* update layers geometry */ | ||
811 | mxr_layer_geo_fix(layer); | ||
812 | mxr_geometry_dump(mdev, &layer->geo); | ||
813 | |||
814 | layer->ops.format_set(layer); | ||
815 | /* enabling layer in hardware */ | ||
816 | spin_lock_irqsave(&layer->enq_slock, flags); | ||
817 | layer->state = MXR_LAYER_STREAMING_START; | ||
818 | spin_unlock_irqrestore(&layer->enq_slock, flags); | ||
819 | |||
820 | return 0; | ||
821 | } | ||
822 | |||
823 | static void mxr_watchdog(unsigned long arg) | ||
824 | { | ||
825 | struct mxr_layer *layer = (struct mxr_layer *) arg; | ||
826 | struct mxr_device *mdev = layer->mdev; | ||
827 | unsigned long flags; | ||
828 | |||
829 | mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name); | ||
830 | |||
831 | spin_lock_irqsave(&layer->enq_slock, flags); | ||
832 | |||
833 | if (layer->update_buf == layer->shadow_buf) | ||
834 | layer->update_buf = NULL; | ||
835 | if (layer->update_buf) { | ||
836 | vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR); | ||
837 | layer->update_buf = NULL; | ||
838 | } | ||
839 | if (layer->shadow_buf) { | ||
840 | vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR); | ||
841 | layer->shadow_buf = NULL; | ||
842 | } | ||
843 | spin_unlock_irqrestore(&layer->enq_slock, flags); | ||
844 | } | ||
845 | |||
846 | static int stop_streaming(struct vb2_queue *vq) | ||
847 | { | ||
848 | struct mxr_layer *layer = vb2_get_drv_priv(vq); | ||
849 | struct mxr_device *mdev = layer->mdev; | ||
850 | unsigned long flags; | ||
851 | struct timer_list watchdog; | ||
852 | struct mxr_buffer *buf, *buf_tmp; | ||
853 | |||
854 | mxr_dbg(mdev, "%s\n", __func__); | ||
855 | |||
856 | spin_lock_irqsave(&layer->enq_slock, flags); | ||
857 | |||
858 | /* reset list */ | ||
859 | layer->state = MXR_LAYER_STREAMING_FINISH; | ||
860 | |||
861 | /* set all buffer to be done */ | ||
862 | list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) { | ||
863 | list_del(&buf->list); | ||
864 | vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); | ||
865 | } | ||
866 | |||
867 | spin_unlock_irqrestore(&layer->enq_slock, flags); | ||
868 | |||
869 | /* give 1 seconds to complete to complete last buffers */ | ||
870 | setup_timer_on_stack(&watchdog, mxr_watchdog, | ||
871 | (unsigned long)layer); | ||
872 | mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000)); | ||
873 | |||
874 | /* wait until all buffers are goes to done state */ | ||
875 | vb2_wait_for_all_buffers(vq); | ||
876 | |||
877 | /* stop timer if all synchronization is done */ | ||
878 | del_timer_sync(&watchdog); | ||
879 | destroy_timer_on_stack(&watchdog); | ||
880 | |||
881 | /* stopping hardware */ | ||
882 | spin_lock_irqsave(&layer->enq_slock, flags); | ||
883 | layer->state = MXR_LAYER_IDLE; | ||
884 | spin_unlock_irqrestore(&layer->enq_slock, flags); | ||
885 | |||
886 | /* disabling layer in hardware */ | ||
887 | layer->ops.stream_set(layer, MXR_DISABLE); | ||
888 | /* remove one streamer */ | ||
889 | mxr_streamer_put(mdev); | ||
890 | /* allow changes in output configuration */ | ||
891 | mxr_output_put(mdev); | ||
892 | return 0; | ||
893 | } | ||
894 | |||
895 | static struct vb2_ops mxr_video_qops = { | ||
896 | .queue_setup = queue_setup, | ||
897 | .buf_queue = buf_queue, | ||
898 | .wait_prepare = wait_unlock, | ||
899 | .wait_finish = wait_lock, | ||
900 | .start_streaming = start_streaming, | ||
901 | .stop_streaming = stop_streaming, | ||
902 | }; | ||
903 | |||
904 | /* FIXME: try to put this functions to mxr_base_layer_create */ | ||
905 | int mxr_base_layer_register(struct mxr_layer *layer) | ||
906 | { | ||
907 | struct mxr_device *mdev = layer->mdev; | ||
908 | int ret; | ||
909 | |||
910 | ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1); | ||
911 | if (ret) | ||
912 | mxr_err(mdev, "failed to register video device\n"); | ||
913 | else | ||
914 | mxr_info(mdev, "registered layer %s as /dev/video%d\n", | ||
915 | layer->vfd.name, layer->vfd.num); | ||
916 | return ret; | ||
917 | } | ||
918 | |||
919 | void mxr_base_layer_unregister(struct mxr_layer *layer) | ||
920 | { | ||
921 | video_unregister_device(&layer->vfd); | ||
922 | } | ||
923 | |||
924 | void mxr_layer_release(struct mxr_layer *layer) | ||
925 | { | ||
926 | if (layer->ops.release) | ||
927 | layer->ops.release(layer); | ||
928 | } | ||
929 | |||
930 | void mxr_base_layer_release(struct mxr_layer *layer) | ||
931 | { | ||
932 | kfree(layer); | ||
933 | } | ||
934 | |||
935 | static void mxr_vfd_release(struct video_device *vdev) | ||
936 | { | ||
937 | printk(KERN_INFO "video device release\n"); | ||
938 | } | ||
939 | |||
940 | struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, | ||
941 | int idx, char *name, struct mxr_layer_ops *ops) | ||
942 | { | ||
943 | struct mxr_layer *layer; | ||
944 | |||
945 | layer = kzalloc(sizeof *layer, GFP_KERNEL); | ||
946 | if (layer == NULL) { | ||
947 | mxr_err(mdev, "not enough memory for layer.\n"); | ||
948 | goto fail; | ||
949 | } | ||
950 | |||
951 | layer->mdev = mdev; | ||
952 | layer->idx = idx; | ||
953 | layer->ops = *ops; | ||
954 | |||
955 | spin_lock_init(&layer->enq_slock); | ||
956 | INIT_LIST_HEAD(&layer->enq_list); | ||
957 | mutex_init(&layer->mutex); | ||
958 | |||
959 | layer->vfd = (struct video_device) { | ||
960 | .minor = -1, | ||
961 | .release = mxr_vfd_release, | ||
962 | .fops = &mxr_fops, | ||
963 | .ioctl_ops = &mxr_ioctl_ops, | ||
964 | }; | ||
965 | strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name)); | ||
966 | /* let framework control PRIORITY */ | ||
967 | set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); | ||
968 | |||
969 | video_set_drvdata(&layer->vfd, layer); | ||
970 | layer->vfd.lock = &layer->mutex; | ||
971 | layer->vfd.v4l2_dev = &mdev->v4l2_dev; | ||
972 | |||
973 | layer->vb_queue = (struct vb2_queue) { | ||
974 | .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, | ||
975 | .io_modes = VB2_MMAP | VB2_USERPTR, | ||
976 | .drv_priv = layer, | ||
977 | .buf_struct_size = sizeof(struct mxr_buffer), | ||
978 | .ops = &mxr_video_qops, | ||
979 | .mem_ops = &vb2_dma_contig_memops, | ||
980 | }; | ||
981 | |||
982 | return layer; | ||
983 | |||
984 | fail: | ||
985 | return NULL; | ||
986 | } | ||
987 | |||
988 | static const struct mxr_format *find_format_by_fourcc( | ||
989 | struct mxr_layer *layer, unsigned long fourcc) | ||
990 | { | ||
991 | int i; | ||
992 | |||
993 | for (i = 0; i < layer->fmt_array_size; ++i) | ||
994 | if (layer->fmt_array[i]->fourcc == fourcc) | ||
995 | return layer->fmt_array[i]; | ||
996 | return NULL; | ||
997 | } | ||
998 | |||
999 | static const struct mxr_format *find_format_by_index( | ||
1000 | struct mxr_layer *layer, unsigned long index) | ||
1001 | { | ||
1002 | if (index >= layer->fmt_array_size) | ||
1003 | return NULL; | ||
1004 | return layer->fmt_array[index]; | ||
1005 | } | ||
1006 | |||
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/video/s5p-tv/mixer_vp_layer.c new file mode 100644 index 00000000000..6950ed8ac1a --- /dev/null +++ b/drivers/media/video/s5p-tv/mixer_vp_layer.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Samsung TV Mixer driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include "mixer.h" | ||
15 | |||
16 | #include "regs-vp.h" | ||
17 | |||
18 | #include <media/videobuf2-dma-contig.h> | ||
19 | |||
20 | /* FORMAT DEFINITIONS */ | ||
21 | static const struct mxr_format mxr_fmt_nv12 = { | ||
22 | .name = "NV12", | ||
23 | .fourcc = V4L2_PIX_FMT_NV12, | ||
24 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
25 | .num_planes = 2, | ||
26 | .plane = { | ||
27 | { .width = 1, .height = 1, .size = 1 }, | ||
28 | { .width = 2, .height = 2, .size = 2 }, | ||
29 | }, | ||
30 | .num_subframes = 1, | ||
31 | .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, | ||
32 | }; | ||
33 | |||
34 | static const struct mxr_format mxr_fmt_nv21 = { | ||
35 | .name = "NV21", | ||
36 | .fourcc = V4L2_PIX_FMT_NV21, | ||
37 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
38 | .num_planes = 2, | ||
39 | .plane = { | ||
40 | { .width = 1, .height = 1, .size = 1 }, | ||
41 | { .width = 2, .height = 2, .size = 2 }, | ||
42 | }, | ||
43 | .num_subframes = 1, | ||
44 | .cookie = VP_MODE_NV21 | VP_MODE_MEM_LINEAR, | ||
45 | }; | ||
46 | |||
47 | static const struct mxr_format mxr_fmt_nv12m = { | ||
48 | .name = "NV12 (mplane)", | ||
49 | .fourcc = V4L2_PIX_FMT_NV12M, | ||
50 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
51 | .num_planes = 2, | ||
52 | .plane = { | ||
53 | { .width = 1, .height = 1, .size = 1 }, | ||
54 | { .width = 2, .height = 2, .size = 2 }, | ||
55 | }, | ||
56 | .num_subframes = 2, | ||
57 | .plane2subframe = {0, 1}, | ||
58 | .cookie = VP_MODE_NV12 | VP_MODE_MEM_LINEAR, | ||
59 | }; | ||
60 | |||
61 | static const struct mxr_format mxr_fmt_nv12mt = { | ||
62 | .name = "NV12 tiled (mplane)", | ||
63 | .fourcc = V4L2_PIX_FMT_NV12MT, | ||
64 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
65 | .num_planes = 2, | ||
66 | .plane = { | ||
67 | { .width = 128, .height = 32, .size = 4096 }, | ||
68 | { .width = 128, .height = 32, .size = 2048 }, | ||
69 | }, | ||
70 | .num_subframes = 2, | ||
71 | .plane2subframe = {0, 1}, | ||
72 | .cookie = VP_MODE_NV12 | VP_MODE_MEM_TILED, | ||
73 | }; | ||
74 | |||
75 | static const struct mxr_format *mxr_video_format[] = { | ||
76 | &mxr_fmt_nv12, | ||
77 | &mxr_fmt_nv21, | ||
78 | &mxr_fmt_nv12m, | ||
79 | &mxr_fmt_nv12mt, | ||
80 | }; | ||
81 | |||
82 | /* AUXILIARY CALLBACKS */ | ||
83 | |||
84 | static void mxr_vp_layer_release(struct mxr_layer *layer) | ||
85 | { | ||
86 | mxr_base_layer_unregister(layer); | ||
87 | mxr_base_layer_release(layer); | ||
88 | } | ||
89 | |||
90 | static void mxr_vp_buffer_set(struct mxr_layer *layer, | ||
91 | struct mxr_buffer *buf) | ||
92 | { | ||
93 | dma_addr_t luma_addr[2] = {0, 0}; | ||
94 | dma_addr_t chroma_addr[2] = {0, 0}; | ||
95 | |||
96 | if (buf == NULL) { | ||
97 | mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); | ||
98 | return; | ||
99 | } | ||
100 | luma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 0); | ||
101 | if (layer->fmt->num_subframes == 2) { | ||
102 | chroma_addr[0] = vb2_dma_contig_plane_paddr(&buf->vb, 1); | ||
103 | } else { | ||
104 | /* FIXME: mxr_get_plane_size compute integer division, | ||
105 | * which is slow and should not be performed in interrupt */ | ||
106 | chroma_addr[0] = luma_addr[0] + mxr_get_plane_size( | ||
107 | &layer->fmt->plane[0], layer->geo.src.full_width, | ||
108 | layer->geo.src.full_height); | ||
109 | } | ||
110 | if (layer->fmt->cookie & VP_MODE_MEM_TILED) { | ||
111 | luma_addr[1] = luma_addr[0] + 0x40; | ||
112 | chroma_addr[1] = chroma_addr[0] + 0x40; | ||
113 | } else { | ||
114 | luma_addr[1] = luma_addr[0] + layer->geo.src.full_width; | ||
115 | chroma_addr[1] = chroma_addr[0]; | ||
116 | } | ||
117 | mxr_reg_vp_buffer(layer->mdev, luma_addr, chroma_addr); | ||
118 | } | ||
119 | |||
120 | static void mxr_vp_stream_set(struct mxr_layer *layer, int en) | ||
121 | { | ||
122 | mxr_reg_vp_layer_stream(layer->mdev, en); | ||
123 | } | ||
124 | |||
125 | static void mxr_vp_format_set(struct mxr_layer *layer) | ||
126 | { | ||
127 | mxr_reg_vp_format(layer->mdev, layer->fmt, &layer->geo); | ||
128 | } | ||
129 | |||
130 | static void mxr_vp_fix_geometry(struct mxr_layer *layer) | ||
131 | { | ||
132 | struct mxr_geometry *geo = &layer->geo; | ||
133 | |||
134 | /* align horizontal size to 8 pixels */ | ||
135 | geo->src.full_width = ALIGN(geo->src.full_width, 8); | ||
136 | /* limit to boundary size */ | ||
137 | geo->src.full_width = clamp_val(geo->src.full_width, 8, 8192); | ||
138 | geo->src.full_height = clamp_val(geo->src.full_height, 1, 8192); | ||
139 | geo->src.width = clamp_val(geo->src.width, 32, geo->src.full_width); | ||
140 | geo->src.width = min(geo->src.width, 2047U); | ||
141 | geo->src.height = clamp_val(geo->src.height, 4, geo->src.full_height); | ||
142 | geo->src.height = min(geo->src.height, 2047U); | ||
143 | |||
144 | /* setting size of output window */ | ||
145 | geo->dst.width = clamp_val(geo->dst.width, 8, geo->dst.full_width); | ||
146 | geo->dst.height = clamp_val(geo->dst.height, 1, geo->dst.full_height); | ||
147 | |||
148 | /* ensure that scaling is in range 1/4x to 16x */ | ||
149 | if (geo->src.width >= 4 * geo->dst.width) | ||
150 | geo->src.width = 4 * geo->dst.width; | ||
151 | if (geo->dst.width >= 16 * geo->src.width) | ||
152 | geo->dst.width = 16 * geo->src.width; | ||
153 | if (geo->src.height >= 4 * geo->dst.height) | ||
154 | geo->src.height = 4 * geo->dst.height; | ||
155 | if (geo->dst.height >= 16 * geo->src.height) | ||
156 | geo->dst.height = 16 * geo->src.height; | ||
157 | |||
158 | /* setting scaling ratio */ | ||
159 | geo->x_ratio = (geo->src.width << 16) / geo->dst.width; | ||
160 | geo->y_ratio = (geo->src.height << 16) / geo->dst.height; | ||
161 | |||
162 | /* adjust offsets */ | ||
163 | geo->src.x_offset = min(geo->src.x_offset, | ||
164 | geo->src.full_width - geo->src.width); | ||
165 | geo->src.y_offset = min(geo->src.y_offset, | ||
166 | geo->src.full_height - geo->src.height); | ||
167 | geo->dst.x_offset = min(geo->dst.x_offset, | ||
168 | geo->dst.full_width - geo->dst.width); | ||
169 | geo->dst.y_offset = min(geo->dst.y_offset, | ||
170 | geo->dst.full_height - geo->dst.height); | ||
171 | } | ||
172 | |||
173 | /* PUBLIC API */ | ||
174 | |||
175 | struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx) | ||
176 | { | ||
177 | struct mxr_layer *layer; | ||
178 | int ret; | ||
179 | struct mxr_layer_ops ops = { | ||
180 | .release = mxr_vp_layer_release, | ||
181 | .buffer_set = mxr_vp_buffer_set, | ||
182 | .stream_set = mxr_vp_stream_set, | ||
183 | .format_set = mxr_vp_format_set, | ||
184 | .fix_geometry = mxr_vp_fix_geometry, | ||
185 | }; | ||
186 | char name[32]; | ||
187 | |||
188 | sprintf(name, "video%d", idx); | ||
189 | |||
190 | layer = mxr_base_layer_create(mdev, idx, name, &ops); | ||
191 | if (layer == NULL) { | ||
192 | mxr_err(mdev, "failed to initialize layer(%d) base\n", idx); | ||
193 | goto fail; | ||
194 | } | ||
195 | |||
196 | layer->fmt_array = mxr_video_format; | ||
197 | layer->fmt_array_size = ARRAY_SIZE(mxr_video_format); | ||
198 | |||
199 | ret = mxr_base_layer_register(layer); | ||
200 | if (ret) | ||
201 | goto fail_layer; | ||
202 | |||
203 | return layer; | ||
204 | |||
205 | fail_layer: | ||
206 | mxr_base_layer_release(layer); | ||
207 | |||
208 | fail: | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h new file mode 100644 index 00000000000..ac93ad6f2bc --- /dev/null +++ b/drivers/media/video/s5p-tv/regs-hdmi.h | |||
@@ -0,0 +1,141 @@ | |||
1 | /* linux/arch/arm/mach-exynos4/include/mach/regs-hdmi.h | ||
2 | * | ||
3 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * HDMI register header file for Samsung TVOUT driver | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef SAMSUNG_REGS_HDMI_H | ||
14 | #define SAMSUNG_REGS_HDMI_H | ||
15 | |||
16 | /* | ||
17 | * Register part | ||
18 | */ | ||
19 | |||
20 | #define HDMI_CTRL_BASE(x) ((x) + 0x00000000) | ||
21 | #define HDMI_CORE_BASE(x) ((x) + 0x00010000) | ||
22 | #define HDMI_TG_BASE(x) ((x) + 0x00050000) | ||
23 | |||
24 | /* Control registers */ | ||
25 | #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) | ||
26 | #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) | ||
27 | #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) | ||
28 | #define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) | ||
29 | #define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) | ||
30 | #define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) | ||
31 | #define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) | ||
32 | |||
33 | /* Core registers */ | ||
34 | #define HDMI_CON_0 HDMI_CORE_BASE(0x0000) | ||
35 | #define HDMI_CON_1 HDMI_CORE_BASE(0x0004) | ||
36 | #define HDMI_CON_2 HDMI_CORE_BASE(0x0008) | ||
37 | #define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) | ||
38 | #define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) | ||
39 | #define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) | ||
40 | #define HDMI_HPD HDMI_CORE_BASE(0x0030) | ||
41 | #define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) | ||
42 | #define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) | ||
43 | #define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) | ||
44 | #define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) | ||
45 | #define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) | ||
46 | #define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) | ||
47 | #define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) | ||
48 | #define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) | ||
49 | #define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) | ||
50 | #define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) | ||
51 | #define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) | ||
52 | #define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) | ||
53 | #define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) | ||
54 | #define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) | ||
55 | #define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) | ||
56 | #define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) | ||
57 | #define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) | ||
58 | #define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) | ||
59 | #define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) | ||
60 | #define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) | ||
61 | #define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) | ||
62 | #define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) | ||
63 | #define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) | ||
64 | #define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) | ||
65 | #define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) | ||
66 | #define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) | ||
67 | #define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) | ||
68 | #define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) | ||
69 | #define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) | ||
70 | #define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) | ||
71 | #define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) | ||
72 | #define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) | ||
73 | #define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) | ||
74 | #define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) | ||
75 | |||
76 | /* Timing generator registers */ | ||
77 | #define HDMI_TG_CMD HDMI_TG_BASE(0x0000) | ||
78 | #define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018) | ||
79 | #define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C) | ||
80 | #define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020) | ||
81 | #define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024) | ||
82 | #define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028) | ||
83 | #define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C) | ||
84 | #define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030) | ||
85 | #define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034) | ||
86 | #define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038) | ||
87 | #define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C) | ||
88 | #define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040) | ||
89 | #define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044) | ||
90 | #define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048) | ||
91 | #define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C) | ||
92 | #define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050) | ||
93 | #define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054) | ||
94 | #define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058) | ||
95 | #define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C) | ||
96 | #define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060) | ||
97 | #define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064) | ||
98 | #define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078) | ||
99 | #define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C) | ||
100 | #define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080) | ||
101 | #define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084) | ||
102 | #define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088) | ||
103 | #define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C) | ||
104 | #define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090) | ||
105 | #define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094) | ||
106 | |||
107 | /* | ||
108 | * Bit definition part | ||
109 | */ | ||
110 | |||
111 | /* HDMI_INTC_CON */ | ||
112 | #define HDMI_INTC_EN_GLOBAL (1 << 6) | ||
113 | #define HDMI_INTC_EN_HPD_PLUG (1 << 3) | ||
114 | #define HDMI_INTC_EN_HPD_UNPLUG (1 << 2) | ||
115 | |||
116 | /* HDMI_INTC_FLAG */ | ||
117 | #define HDMI_INTC_FLAG_HPD_PLUG (1 << 3) | ||
118 | #define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2) | ||
119 | |||
120 | /* HDMI_PHY_RSTOUT */ | ||
121 | #define HDMI_PHY_SW_RSTOUT (1 << 0) | ||
122 | |||
123 | /* HDMI_CORE_RSTOUT */ | ||
124 | #define HDMI_CORE_SW_RSTOUT (1 << 0) | ||
125 | |||
126 | /* HDMI_CON_0 */ | ||
127 | #define HDMI_BLUE_SCR_EN (1 << 5) | ||
128 | #define HDMI_EN (1 << 0) | ||
129 | |||
130 | /* HDMI_PHY_STATUS */ | ||
131 | #define HDMI_PHY_STATUS_READY (1 << 0) | ||
132 | |||
133 | /* HDMI_MODE_SEL */ | ||
134 | #define HDMI_MODE_HDMI_EN (1 << 1) | ||
135 | #define HDMI_MODE_DVI_EN (1 << 0) | ||
136 | #define HDMI_MODE_MASK (3 << 0) | ||
137 | |||
138 | /* HDMI_TG_CMD */ | ||
139 | #define HDMI_TG_EN (1 << 0) | ||
140 | |||
141 | #endif /* SAMSUNG_REGS_HDMI_H */ | ||
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h new file mode 100644 index 00000000000..3c8442609c1 --- /dev/null +++ b/drivers/media/video/s5p-tv/regs-mixer.h | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * Mixer register header file for Samsung Mixer driver | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef SAMSUNG_REGS_MIXER_H | ||
12 | #define SAMSUNG_REGS_MIXER_H | ||
13 | |||
14 | /* | ||
15 | * Register part | ||
16 | */ | ||
17 | #define MXR_STATUS 0x0000 | ||
18 | #define MXR_CFG 0x0004 | ||
19 | #define MXR_INT_EN 0x0008 | ||
20 | #define MXR_INT_STATUS 0x000C | ||
21 | #define MXR_LAYER_CFG 0x0010 | ||
22 | #define MXR_VIDEO_CFG 0x0014 | ||
23 | #define MXR_GRAPHIC0_CFG 0x0020 | ||
24 | #define MXR_GRAPHIC0_BASE 0x0024 | ||
25 | #define MXR_GRAPHIC0_SPAN 0x0028 | ||
26 | #define MXR_GRAPHIC0_SXY 0x002C | ||
27 | #define MXR_GRAPHIC0_WH 0x0030 | ||
28 | #define MXR_GRAPHIC0_DXY 0x0034 | ||
29 | #define MXR_GRAPHIC0_BLANK 0x0038 | ||
30 | #define MXR_GRAPHIC1_CFG 0x0040 | ||
31 | #define MXR_GRAPHIC1_BASE 0x0044 | ||
32 | #define MXR_GRAPHIC1_SPAN 0x0048 | ||
33 | #define MXR_GRAPHIC1_SXY 0x004C | ||
34 | #define MXR_GRAPHIC1_WH 0x0050 | ||
35 | #define MXR_GRAPHIC1_DXY 0x0054 | ||
36 | #define MXR_GRAPHIC1_BLANK 0x0058 | ||
37 | #define MXR_BG_CFG 0x0060 | ||
38 | #define MXR_BG_COLOR0 0x0064 | ||
39 | #define MXR_BG_COLOR1 0x0068 | ||
40 | #define MXR_BG_COLOR2 0x006C | ||
41 | |||
42 | /* for parametrized access to layer registers */ | ||
43 | #define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20) | ||
44 | #define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20) | ||
45 | #define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20) | ||
46 | #define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20) | ||
47 | #define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20) | ||
48 | #define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20) | ||
49 | |||
50 | /* | ||
51 | * Bit definition part | ||
52 | */ | ||
53 | |||
54 | /* generates mask for range of bits */ | ||
55 | #define MXR_MASK(high_bit, low_bit) \ | ||
56 | (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) | ||
57 | |||
58 | #define MXR_MASK_VAL(val, high_bit, low_bit) \ | ||
59 | (((val) << (low_bit)) & MXR_MASK(high_bit, low_bit)) | ||
60 | |||
61 | /* bits for MXR_STATUS */ | ||
62 | #define MXR_STATUS_16_BURST (1 << 7) | ||
63 | #define MXR_STATUS_BURST_MASK (1 << 7) | ||
64 | #define MXR_STATUS_SYNC_ENABLE (1 << 2) | ||
65 | #define MXR_STATUS_REG_RUN (1 << 0) | ||
66 | |||
67 | /* bits for MXR_CFG */ | ||
68 | #define MXR_CFG_OUT_YUV444 (0 << 8) | ||
69 | #define MXR_CFG_OUT_RGB888 (1 << 8) | ||
70 | #define MXR_CFG_DST_SDO (0 << 7) | ||
71 | #define MXR_CFG_DST_HDMI (1 << 7) | ||
72 | #define MXR_CFG_DST_MASK (1 << 7) | ||
73 | #define MXR_CFG_SCAN_HD_720 (0 << 6) | ||
74 | #define MXR_CFG_SCAN_HD_1080 (1 << 6) | ||
75 | #define MXR_CFG_GRP1_ENABLE (1 << 5) | ||
76 | #define MXR_CFG_GRP0_ENABLE (1 << 4) | ||
77 | #define MXR_CFG_VP_ENABLE (1 << 3) | ||
78 | #define MXR_CFG_SCAN_INTERLACE (0 << 2) | ||
79 | #define MXR_CFG_SCAN_PROGRASSIVE (1 << 2) | ||
80 | #define MXR_CFG_SCAN_NTSC (0 << 1) | ||
81 | #define MXR_CFG_SCAN_PAL (1 << 1) | ||
82 | #define MXR_CFG_SCAN_SD (0 << 0) | ||
83 | #define MXR_CFG_SCAN_HD (1 << 0) | ||
84 | #define MXR_CFG_SCAN_MASK 0x47 | ||
85 | |||
86 | /* bits for MXR_GRAPHICn_CFG */ | ||
87 | #define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21) | ||
88 | #define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20) | ||
89 | #define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8) | ||
90 | #define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0) | ||
91 | #define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0) | ||
92 | |||
93 | /* bits for MXR_GRAPHICn_WH */ | ||
94 | #define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28) | ||
95 | #define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12) | ||
96 | #define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) | ||
97 | #define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) | ||
98 | |||
99 | /* bits for MXR_GRAPHICn_SXY */ | ||
100 | #define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) | ||
101 | #define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) | ||
102 | |||
103 | /* bits for MXR_GRAPHICn_DXY */ | ||
104 | #define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16) | ||
105 | #define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0) | ||
106 | |||
107 | /* bits for MXR_INT_EN */ | ||
108 | #define MXR_INT_EN_VSYNC (1 << 11) | ||
109 | #define MXR_INT_EN_ALL (0x0f << 8) | ||
110 | |||
111 | /* bit for MXR_INT_STATUS */ | ||
112 | #define MXR_INT_CLEAR_VSYNC (1 << 11) | ||
113 | #define MXR_INT_STATUS_VSYNC (1 << 0) | ||
114 | |||
115 | /* bit for MXR_LAYER_CFG */ | ||
116 | #define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8) | ||
117 | #define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4) | ||
118 | #define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0) | ||
119 | |||
120 | #endif /* SAMSUNG_REGS_MIXER_H */ | ||
121 | |||
diff --git a/drivers/media/video/s5p-tv/regs-sdo.h b/drivers/media/video/s5p-tv/regs-sdo.h new file mode 100644 index 00000000000..7f7c2b8ac14 --- /dev/null +++ b/drivers/media/video/s5p-tv/regs-sdo.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* drivers/media/video/s5p-tv/regs-sdo.h | ||
2 | * | ||
3 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * SDO register description file | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef SAMSUNG_REGS_SDO_H | ||
14 | #define SAMSUNG_REGS_SDO_H | ||
15 | |||
16 | /* | ||
17 | * Register part | ||
18 | */ | ||
19 | |||
20 | #define SDO_CLKCON 0x0000 | ||
21 | #define SDO_CONFIG 0x0008 | ||
22 | #define SDO_VBI 0x0014 | ||
23 | #define SDO_DAC 0x003C | ||
24 | #define SDO_CCCON 0x0180 | ||
25 | #define SDO_IRQ 0x0280 | ||
26 | #define SDO_IRQMASK 0x0284 | ||
27 | #define SDO_VERSION 0x03D8 | ||
28 | |||
29 | /* | ||
30 | * Bit definition part | ||
31 | */ | ||
32 | |||
33 | /* SDO Clock Control Register (SDO_CLKCON) */ | ||
34 | #define SDO_TVOUT_SW_RESET (1 << 4) | ||
35 | #define SDO_TVOUT_CLOCK_READY (1 << 1) | ||
36 | #define SDO_TVOUT_CLOCK_ON (1 << 0) | ||
37 | |||
38 | /* SDO Video Standard Configuration Register (SDO_CONFIG) */ | ||
39 | #define SDO_PROGRESSIVE (1 << 4) | ||
40 | #define SDO_NTSC_M 0 | ||
41 | #define SDO_PAL_M 1 | ||
42 | #define SDO_PAL_BGHID 2 | ||
43 | #define SDO_PAL_N 3 | ||
44 | #define SDO_PAL_NC 4 | ||
45 | #define SDO_NTSC_443 8 | ||
46 | #define SDO_PAL_60 9 | ||
47 | #define SDO_STANDARD_MASK 0xf | ||
48 | |||
49 | /* SDO VBI Configuration Register (SDO_VBI) */ | ||
50 | #define SDO_CVBS_WSS_INS (1 << 14) | ||
51 | #define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12) | ||
52 | |||
53 | /* SDO DAC Configuration Register (SDO_DAC) */ | ||
54 | #define SDO_POWER_ON_DAC (1 << 0) | ||
55 | |||
56 | /* SDO Color Compensation On/Off Control (SDO_CCCON) */ | ||
57 | #define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4) | ||
58 | #define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0) | ||
59 | |||
60 | /* SDO Interrupt Request Register (SDO_IRQ) */ | ||
61 | #define SDO_VSYNC_IRQ_PEND (1 << 0) | ||
62 | |||
63 | #endif /* SAMSUNG_REGS_SDO_H */ | ||
diff --git a/drivers/media/video/s5p-tv/regs-vp.h b/drivers/media/video/s5p-tv/regs-vp.h new file mode 100644 index 00000000000..6c63984e11e --- /dev/null +++ b/drivers/media/video/s5p-tv/regs-vp.h | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * Video processor register header file for Samsung Mixer driver | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef SAMSUNG_REGS_VP_H | ||
13 | #define SAMSUNG_REGS_VP_H | ||
14 | |||
15 | /* | ||
16 | * Register part | ||
17 | */ | ||
18 | |||
19 | #define VP_ENABLE 0x0000 | ||
20 | #define VP_SRESET 0x0004 | ||
21 | #define VP_SHADOW_UPDATE 0x0008 | ||
22 | #define VP_FIELD_ID 0x000C | ||
23 | #define VP_MODE 0x0010 | ||
24 | #define VP_IMG_SIZE_Y 0x0014 | ||
25 | #define VP_IMG_SIZE_C 0x0018 | ||
26 | #define VP_PER_RATE_CTRL 0x001C | ||
27 | #define VP_TOP_Y_PTR 0x0028 | ||
28 | #define VP_BOT_Y_PTR 0x002C | ||
29 | #define VP_TOP_C_PTR 0x0030 | ||
30 | #define VP_BOT_C_PTR 0x0034 | ||
31 | #define VP_ENDIAN_MODE 0x03CC | ||
32 | #define VP_SRC_H_POSITION 0x0044 | ||
33 | #define VP_SRC_V_POSITION 0x0048 | ||
34 | #define VP_SRC_WIDTH 0x004C | ||
35 | #define VP_SRC_HEIGHT 0x0050 | ||
36 | #define VP_DST_H_POSITION 0x0054 | ||
37 | #define VP_DST_V_POSITION 0x0058 | ||
38 | #define VP_DST_WIDTH 0x005C | ||
39 | #define VP_DST_HEIGHT 0x0060 | ||
40 | #define VP_H_RATIO 0x0064 | ||
41 | #define VP_V_RATIO 0x0068 | ||
42 | #define VP_POLY8_Y0_LL 0x006C | ||
43 | #define VP_POLY4_Y0_LL 0x00EC | ||
44 | #define VP_POLY4_C0_LL 0x012C | ||
45 | |||
46 | /* | ||
47 | * Bit definition part | ||
48 | */ | ||
49 | |||
50 | /* generates mask for range of bits */ | ||
51 | |||
52 | #define VP_MASK(high_bit, low_bit) \ | ||
53 | (((2 << ((high_bit) - (low_bit))) - 1) << (low_bit)) | ||
54 | |||
55 | #define VP_MASK_VAL(val, high_bit, low_bit) \ | ||
56 | (((val) << (low_bit)) & VP_MASK(high_bit, low_bit)) | ||
57 | |||
58 | /* VP_ENABLE */ | ||
59 | #define VP_ENABLE_ON (1 << 0) | ||
60 | |||
61 | /* VP_SRESET */ | ||
62 | #define VP_SRESET_PROCESSING (1 << 0) | ||
63 | |||
64 | /* VP_SHADOW_UPDATE */ | ||
65 | #define VP_SHADOW_UPDATE_ENABLE (1 << 0) | ||
66 | |||
67 | /* VP_MODE */ | ||
68 | #define VP_MODE_NV12 (0 << 6) | ||
69 | #define VP_MODE_NV21 (1 << 6) | ||
70 | #define VP_MODE_LINE_SKIP (1 << 5) | ||
71 | #define VP_MODE_MEM_LINEAR (0 << 4) | ||
72 | #define VP_MODE_MEM_TILED (1 << 4) | ||
73 | #define VP_MODE_FMT_MASK (5 << 4) | ||
74 | #define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2) | ||
75 | #define VP_MODE_2D_IPC (1 << 1) | ||
76 | |||
77 | /* VP_IMG_SIZE_Y */ | ||
78 | /* VP_IMG_SIZE_C */ | ||
79 | #define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16) | ||
80 | #define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0) | ||
81 | |||
82 | /* VP_SRC_H_POSITION */ | ||
83 | #define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4) | ||
84 | |||
85 | /* VP_ENDIAN_MODE */ | ||
86 | #define VP_ENDIAN_MODE_LITTLE (1 << 0) | ||
87 | |||
88 | #endif /* SAMSUNG_REGS_VP_H */ | ||
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c new file mode 100644 index 00000000000..4dddd6bd635 --- /dev/null +++ b/drivers/media/video/s5p-tv/sdo_drv.c | |||
@@ -0,0 +1,479 @@ | |||
1 | /* | ||
2 | * Samsung Standard Definition Output (SDO) driver | ||
3 | * | ||
4 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
5 | * | ||
6 | * Tomasz Stanislawski, <t.stanislaws@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published | ||
10 | * by the Free Software Foundiation. either version 2 of the License, | ||
11 | * or (at your option) any later version | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/regulator/consumer.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #include <media/v4l2-subdev.h> | ||
27 | |||
28 | #include "regs-sdo.h" | ||
29 | |||
30 | MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>"); | ||
31 | MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | #define SDO_DEFAULT_STD V4L2_STD_PAL | ||
35 | |||
36 | struct sdo_format { | ||
37 | v4l2_std_id id; | ||
38 | /* all modes are 720 pixels wide */ | ||
39 | unsigned int height; | ||
40 | unsigned int cookie; | ||
41 | }; | ||
42 | |||
43 | struct sdo_device { | ||
44 | /** pointer to device parent */ | ||
45 | struct device *dev; | ||
46 | /** base address of SDO registers */ | ||
47 | void __iomem *regs; | ||
48 | /** SDO interrupt */ | ||
49 | unsigned int irq; | ||
50 | /** DAC source clock */ | ||
51 | struct clk *sclk_dac; | ||
52 | /** DAC clock */ | ||
53 | struct clk *dac; | ||
54 | /** DAC physical interface */ | ||
55 | struct clk *dacphy; | ||
56 | /** clock for control of VPLL */ | ||
57 | struct clk *fout_vpll; | ||
58 | /** regulator for SDO IP power */ | ||
59 | struct regulator *vdac; | ||
60 | /** regulator for SDO plug detection */ | ||
61 | struct regulator *vdet; | ||
62 | /** subdev used as device interface */ | ||
63 | struct v4l2_subdev sd; | ||
64 | /** current format */ | ||
65 | const struct sdo_format *fmt; | ||
66 | }; | ||
67 | |||
68 | static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd) | ||
69 | { | ||
70 | return container_of(sd, struct sdo_device, sd); | ||
71 | } | ||
72 | |||
73 | static inline | ||
74 | void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask) | ||
75 | { | ||
76 | u32 old = readl(sdev->regs + reg_id); | ||
77 | value = (value & mask) | (old & ~mask); | ||
78 | writel(value, sdev->regs + reg_id); | ||
79 | } | ||
80 | |||
81 | static inline | ||
82 | void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value) | ||
83 | { | ||
84 | writel(value, sdev->regs + reg_id); | ||
85 | } | ||
86 | |||
87 | static inline | ||
88 | u32 sdo_read(struct sdo_device *sdev, u32 reg_id) | ||
89 | { | ||
90 | return readl(sdev->regs + reg_id); | ||
91 | } | ||
92 | |||
93 | static irqreturn_t sdo_irq_handler(int irq, void *dev_data) | ||
94 | { | ||
95 | struct sdo_device *sdev = dev_data; | ||
96 | |||
97 | /* clear interrupt */ | ||
98 | sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND); | ||
99 | return IRQ_HANDLED; | ||
100 | } | ||
101 | |||
102 | static void sdo_reg_debug(struct sdo_device *sdev) | ||
103 | { | ||
104 | #define DBGREG(reg_id) \ | ||
105 | dev_info(sdev->dev, #reg_id " = %08x\n", \ | ||
106 | sdo_read(sdev, reg_id)) | ||
107 | |||
108 | DBGREG(SDO_CLKCON); | ||
109 | DBGREG(SDO_CONFIG); | ||
110 | DBGREG(SDO_VBI); | ||
111 | DBGREG(SDO_DAC); | ||
112 | DBGREG(SDO_IRQ); | ||
113 | DBGREG(SDO_IRQMASK); | ||
114 | DBGREG(SDO_VERSION); | ||
115 | } | ||
116 | |||
117 | static const struct sdo_format sdo_format[] = { | ||
118 | { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N }, | ||
119 | { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC }, | ||
120 | { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M }, | ||
121 | { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 }, | ||
122 | { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 }, | ||
123 | { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID }, | ||
124 | { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M }, | ||
125 | }; | ||
126 | |||
127 | static const struct sdo_format *sdo_find_format(v4l2_std_id id) | ||
128 | { | ||
129 | int i; | ||
130 | for (i = 0; i < ARRAY_SIZE(sdo_format); ++i) | ||
131 | if (sdo_format[i].id & id) | ||
132 | return &sdo_format[i]; | ||
133 | return NULL; | ||
134 | } | ||
135 | |||
136 | static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std) | ||
137 | { | ||
138 | *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL | | ||
139 | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | | ||
140 | V4L2_STD_NTSC_443 | V4L2_STD_PAL_60; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) | ||
145 | { | ||
146 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
147 | const struct sdo_format *fmt; | ||
148 | fmt = sdo_find_format(std); | ||
149 | if (fmt == NULL) | ||
150 | return -EINVAL; | ||
151 | sdev->fmt = fmt; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std) | ||
156 | { | ||
157 | *std = sd_to_sdev(sd)->fmt->id; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int sdo_g_mbus_fmt(struct v4l2_subdev *sd, | ||
162 | struct v4l2_mbus_framefmt *fmt) | ||
163 | { | ||
164 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
165 | |||
166 | if (!sdev->fmt) | ||
167 | return -ENXIO; | ||
168 | /* all modes are 720 pixels wide */ | ||
169 | fmt->width = 720; | ||
170 | fmt->height = sdev->fmt->height; | ||
171 | fmt->code = V4L2_MBUS_FMT_FIXED; | ||
172 | fmt->field = V4L2_FIELD_INTERLACED; | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int sdo_s_power(struct v4l2_subdev *sd, int on) | ||
177 | { | ||
178 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
179 | struct device *dev = sdev->dev; | ||
180 | int ret; | ||
181 | |||
182 | dev_info(dev, "sdo_s_power(%d)\n", on); | ||
183 | |||
184 | if (on) | ||
185 | ret = pm_runtime_get_sync(dev); | ||
186 | else | ||
187 | ret = pm_runtime_put_sync(dev); | ||
188 | |||
189 | /* only values < 0 indicate errors */ | ||
190 | return IS_ERR_VALUE(ret) ? ret : 0; | ||
191 | } | ||
192 | |||
193 | static int sdo_streamon(struct sdo_device *sdev) | ||
194 | { | ||
195 | /* set proper clock for Timing Generator */ | ||
196 | clk_set_rate(sdev->fout_vpll, 54000000); | ||
197 | dev_info(sdev->dev, "fout_vpll.rate = %lu\n", | ||
198 | clk_get_rate(sdev->fout_vpll)); | ||
199 | /* enable clock in SDO */ | ||
200 | sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON); | ||
201 | clk_enable(sdev->dacphy); | ||
202 | /* enable DAC */ | ||
203 | sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC); | ||
204 | sdo_reg_debug(sdev); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int sdo_streamoff(struct sdo_device *sdev) | ||
209 | { | ||
210 | int tries; | ||
211 | |||
212 | sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC); | ||
213 | clk_disable(sdev->dacphy); | ||
214 | sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON); | ||
215 | for (tries = 100; tries; --tries) { | ||
216 | if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY) | ||
217 | break; | ||
218 | mdelay(1); | ||
219 | } | ||
220 | if (tries == 0) | ||
221 | dev_err(sdev->dev, "failed to stop streaming\n"); | ||
222 | return tries ? 0 : -EIO; | ||
223 | } | ||
224 | |||
225 | static int sdo_s_stream(struct v4l2_subdev *sd, int on) | ||
226 | { | ||
227 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
228 | return on ? sdo_streamon(sdev) : sdo_streamoff(sdev); | ||
229 | } | ||
230 | |||
231 | static const struct v4l2_subdev_core_ops sdo_sd_core_ops = { | ||
232 | .s_power = sdo_s_power, | ||
233 | }; | ||
234 | |||
235 | static const struct v4l2_subdev_video_ops sdo_sd_video_ops = { | ||
236 | .s_std_output = sdo_s_std_output, | ||
237 | .g_std_output = sdo_g_std_output, | ||
238 | .g_tvnorms_output = sdo_g_tvnorms_output, | ||
239 | .g_mbus_fmt = sdo_g_mbus_fmt, | ||
240 | .s_stream = sdo_s_stream, | ||
241 | }; | ||
242 | |||
243 | static const struct v4l2_subdev_ops sdo_sd_ops = { | ||
244 | .core = &sdo_sd_core_ops, | ||
245 | .video = &sdo_sd_video_ops, | ||
246 | }; | ||
247 | |||
248 | static int sdo_runtime_suspend(struct device *dev) | ||
249 | { | ||
250 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
251 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
252 | |||
253 | dev_info(dev, "suspend\n"); | ||
254 | regulator_disable(sdev->vdet); | ||
255 | regulator_disable(sdev->vdac); | ||
256 | clk_disable(sdev->sclk_dac); | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static int sdo_runtime_resume(struct device *dev) | ||
261 | { | ||
262 | struct v4l2_subdev *sd = dev_get_drvdata(dev); | ||
263 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
264 | |||
265 | dev_info(dev, "resume\n"); | ||
266 | clk_enable(sdev->sclk_dac); | ||
267 | regulator_enable(sdev->vdac); | ||
268 | regulator_enable(sdev->vdet); | ||
269 | |||
270 | /* software reset */ | ||
271 | sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET); | ||
272 | mdelay(10); | ||
273 | sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET); | ||
274 | |||
275 | /* setting TV mode */ | ||
276 | sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK); | ||
277 | /* XXX: forcing interlaced mode using undocumented bit */ | ||
278 | sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE); | ||
279 | /* turn all VBI off */ | ||
280 | sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS | | ||
281 | SDO_CVBS_CLOSED_CAPTION_MASK); | ||
282 | /* turn all post processing off */ | ||
283 | sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF | | ||
284 | SDO_COMPENSATION_CVBS_COMP_OFF); | ||
285 | sdo_reg_debug(sdev); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static const struct dev_pm_ops sdo_pm_ops = { | ||
290 | .runtime_suspend = sdo_runtime_suspend, | ||
291 | .runtime_resume = sdo_runtime_resume, | ||
292 | }; | ||
293 | |||
294 | static int __devinit sdo_probe(struct platform_device *pdev) | ||
295 | { | ||
296 | struct device *dev = &pdev->dev; | ||
297 | struct sdo_device *sdev; | ||
298 | struct resource *res; | ||
299 | int ret = 0; | ||
300 | struct clk *sclk_vpll; | ||
301 | |||
302 | dev_info(dev, "probe start\n"); | ||
303 | sdev = kzalloc(sizeof *sdev, GFP_KERNEL); | ||
304 | if (!sdev) { | ||
305 | dev_err(dev, "not enough memory.\n"); | ||
306 | ret = -ENOMEM; | ||
307 | goto fail; | ||
308 | } | ||
309 | sdev->dev = dev; | ||
310 | |||
311 | /* mapping registers */ | ||
312 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
313 | if (res == NULL) { | ||
314 | dev_err(dev, "get memory resource failed.\n"); | ||
315 | ret = -ENXIO; | ||
316 | goto fail_sdev; | ||
317 | } | ||
318 | |||
319 | sdev->regs = ioremap(res->start, resource_size(res)); | ||
320 | if (sdev->regs == NULL) { | ||
321 | dev_err(dev, "register mapping failed.\n"); | ||
322 | ret = -ENXIO; | ||
323 | goto fail_sdev; | ||
324 | } | ||
325 | |||
326 | /* acquiring interrupt */ | ||
327 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
328 | if (res == NULL) { | ||
329 | dev_err(dev, "get interrupt resource failed.\n"); | ||
330 | ret = -ENXIO; | ||
331 | goto fail_regs; | ||
332 | } | ||
333 | ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev); | ||
334 | if (ret) { | ||
335 | dev_err(dev, "request interrupt failed.\n"); | ||
336 | goto fail_regs; | ||
337 | } | ||
338 | sdev->irq = res->start; | ||
339 | |||
340 | /* acquire clocks */ | ||
341 | sdev->sclk_dac = clk_get(dev, "sclk_dac"); | ||
342 | if (IS_ERR_OR_NULL(sdev->sclk_dac)) { | ||
343 | dev_err(dev, "failed to get clock 'sclk_dac'\n"); | ||
344 | ret = -ENXIO; | ||
345 | goto fail_irq; | ||
346 | } | ||
347 | sdev->dac = clk_get(dev, "dac"); | ||
348 | if (IS_ERR_OR_NULL(sdev->dac)) { | ||
349 | dev_err(dev, "failed to get clock 'dac'\n"); | ||
350 | ret = -ENXIO; | ||
351 | goto fail_sclk_dac; | ||
352 | } | ||
353 | sdev->dacphy = clk_get(dev, "dacphy"); | ||
354 | if (IS_ERR_OR_NULL(sdev->dacphy)) { | ||
355 | dev_err(dev, "failed to get clock 'dacphy'\n"); | ||
356 | ret = -ENXIO; | ||
357 | goto fail_dac; | ||
358 | } | ||
359 | sclk_vpll = clk_get(dev, "sclk_vpll"); | ||
360 | if (IS_ERR_OR_NULL(sclk_vpll)) { | ||
361 | dev_err(dev, "failed to get clock 'sclk_vpll'\n"); | ||
362 | ret = -ENXIO; | ||
363 | goto fail_dacphy; | ||
364 | } | ||
365 | clk_set_parent(sdev->sclk_dac, sclk_vpll); | ||
366 | clk_put(sclk_vpll); | ||
367 | sdev->fout_vpll = clk_get(dev, "fout_vpll"); | ||
368 | if (IS_ERR_OR_NULL(sdev->fout_vpll)) { | ||
369 | dev_err(dev, "failed to get clock 'fout_vpll'\n"); | ||
370 | goto fail_dacphy; | ||
371 | } | ||
372 | dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll)); | ||
373 | |||
374 | /* acquire regulator */ | ||
375 | sdev->vdac = regulator_get(dev, "vdd33a_dac"); | ||
376 | if (IS_ERR_OR_NULL(sdev->vdac)) { | ||
377 | dev_err(dev, "failed to get regulator 'vdac'\n"); | ||
378 | goto fail_fout_vpll; | ||
379 | } | ||
380 | sdev->vdet = regulator_get(dev, "vdet"); | ||
381 | if (IS_ERR_OR_NULL(sdev->vdet)) { | ||
382 | dev_err(dev, "failed to get regulator 'vdet'\n"); | ||
383 | goto fail_vdac; | ||
384 | } | ||
385 | |||
386 | /* enable gate for dac clock, because mixer uses it */ | ||
387 | clk_enable(sdev->dac); | ||
388 | |||
389 | /* configure power management */ | ||
390 | pm_runtime_enable(dev); | ||
391 | |||
392 | /* configuration of interface subdevice */ | ||
393 | v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); | ||
394 | sdev->sd.owner = THIS_MODULE; | ||
395 | strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); | ||
396 | |||
397 | /* set default format */ | ||
398 | sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); | ||
399 | BUG_ON(sdev->fmt == NULL); | ||
400 | |||
401 | /* keeping subdev in device's private for use by other drivers */ | ||
402 | dev_set_drvdata(dev, &sdev->sd); | ||
403 | |||
404 | dev_info(dev, "probe succeeded\n"); | ||
405 | return 0; | ||
406 | |||
407 | fail_vdac: | ||
408 | regulator_put(sdev->vdac); | ||
409 | fail_fout_vpll: | ||
410 | clk_put(sdev->fout_vpll); | ||
411 | fail_dacphy: | ||
412 | clk_put(sdev->dacphy); | ||
413 | fail_dac: | ||
414 | clk_put(sdev->dac); | ||
415 | fail_sclk_dac: | ||
416 | clk_put(sdev->sclk_dac); | ||
417 | fail_irq: | ||
418 | free_irq(sdev->irq, sdev); | ||
419 | fail_regs: | ||
420 | iounmap(sdev->regs); | ||
421 | fail_sdev: | ||
422 | kfree(sdev); | ||
423 | fail: | ||
424 | dev_info(dev, "probe failed\n"); | ||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static int __devexit sdo_remove(struct platform_device *pdev) | ||
429 | { | ||
430 | struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev); | ||
431 | struct sdo_device *sdev = sd_to_sdev(sd); | ||
432 | |||
433 | pm_runtime_disable(&pdev->dev); | ||
434 | clk_disable(sdev->dac); | ||
435 | regulator_put(sdev->vdet); | ||
436 | regulator_put(sdev->vdac); | ||
437 | clk_put(sdev->fout_vpll); | ||
438 | clk_put(sdev->dacphy); | ||
439 | clk_put(sdev->dac); | ||
440 | clk_put(sdev->sclk_dac); | ||
441 | free_irq(sdev->irq, sdev); | ||
442 | iounmap(sdev->regs); | ||
443 | kfree(sdev); | ||
444 | |||
445 | dev_info(&pdev->dev, "remove successful\n"); | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static struct platform_driver sdo_driver __refdata = { | ||
450 | .probe = sdo_probe, | ||
451 | .remove = __devexit_p(sdo_remove), | ||
452 | .driver = { | ||
453 | .name = "s5p-sdo", | ||
454 | .owner = THIS_MODULE, | ||
455 | .pm = &sdo_pm_ops, | ||
456 | } | ||
457 | }; | ||
458 | |||
459 | static int __init sdo_init(void) | ||
460 | { | ||
461 | int ret; | ||
462 | static const char banner[] __initdata = KERN_INFO \ | ||
463 | "Samsung Standard Definition Output (SDO) driver, " | ||
464 | "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; | ||
465 | printk(banner); | ||
466 | |||
467 | ret = platform_driver_register(&sdo_driver); | ||
468 | if (ret) | ||
469 | printk(KERN_ERR "SDO platform driver register failed\n"); | ||
470 | |||
471 | return ret; | ||
472 | } | ||
473 | module_init(sdo_init); | ||
474 | |||
475 | static void __exit sdo_exit(void) | ||
476 | { | ||
477 | platform_driver_unregister(&sdo_driver); | ||
478 | } | ||
479 | module_exit(sdo_exit); | ||