diff options
Diffstat (limited to 'drivers/media/video')
-rw-r--r-- | drivers/media/video/s5p-tv/Kconfig | 16 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/Makefile | 2 | ||||
-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-mixer.h | 121 | ||||
-rw-r--r-- | drivers/media/video/s5p-tv/regs-vp.h | 88 |
10 files changed, 3011 insertions, 0 deletions
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig index 9c1f6343f077..9c37dee7bc59 100644 --- a/drivers/media/video/s5p-tv/Kconfig +++ b/drivers/media/video/s5p-tv/Kconfig | |||
@@ -57,4 +57,20 @@ config VIDEO_SAMSUNG_S5P_SDO | |||
57 | subdev for use by other drivers. This driver requires | 57 | subdev for use by other drivers. This driver requires |
58 | hdmiphy driver to work correctly. | 58 | hdmiphy driver to work correctly. |
59 | 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 | |||
60 | endif # VIDEO_SAMSUNG_S5P_TV | 76 | endif # VIDEO_SAMSUNG_S5P_TV |
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile index c874d16be9fd..37e4c17663b4 100644 --- a/drivers/media/video/s5p-tv/Makefile +++ b/drivers/media/video/s5p-tv/Makefile | |||
@@ -12,4 +12,6 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o | |||
12 | s5p-hdmi-y += hdmi_drv.o | 12 | s5p-hdmi-y += hdmi_drv.o |
13 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o | 13 | obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o |
14 | s5p-sdo-y += sdo_drv.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 | ||
15 | 17 | ||
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h new file mode 100644 index 000000000000..e2242243f63d --- /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 000000000000..00643094b221 --- /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 000000000000..58f0ba49580f --- /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 000000000000..38dac672aa1c --- /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 000000000000..43ac22f35bc7 --- /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 000000000000..6950ed8ac1a0 --- /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-mixer.h b/drivers/media/video/s5p-tv/regs-mixer.h new file mode 100644 index 000000000000..3c8442609c1a --- /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-vp.h b/drivers/media/video/s5p-tv/regs-vp.h new file mode 100644 index 000000000000..6c63984e11e8 --- /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 */ | ||