Merge tag 'v3.3.7' into 3.3/master
[zen-stable.git] / drivers / media / video / s5p-tv / mixer_drv.c
blob00643094b2212f69d637b60bed06875c8401cdb5
1 /*
2 * Samsung TV Mixer driver
4 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
6 * Tomasz Stanislawski, <t.stanislaws@samsung.com>
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
14 #include "mixer.h"
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>
26 MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
27 MODULE_DESCRIPTION("Samsung MIXER");
28 MODULE_LICENSE("GPL");
30 /* --------- DRIVER PARAMETERS ---------- */
32 static struct mxr_output_conf mxr_output_conf[] = {
34 .output_name = "S5P HDMI connector",
35 .module_name = "s5p-hdmi",
36 .cookie = 1,
39 .output_name = "S5P SDO connector",
40 .module_name = "s5p-sdo",
41 .cookie = 0,
45 void mxr_get_mbus_fmt(struct mxr_device *mdev,
46 struct v4l2_mbus_framefmt *mbus_fmt)
48 struct v4l2_subdev *sd;
49 int ret;
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);
58 void mxr_streamer_get(struct mxr_device *mdev)
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;
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);
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);
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);
85 mutex_unlock(&mdev->mutex);
86 mxr_reg_dump(mdev);
87 /* FIXME: what to do when streaming fails? */
90 void mxr_streamer_put(struct mxr_device *mdev)
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);
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);
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);
112 void mxr_output_get(struct mxr_device *mdev)
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);
123 void mxr_output_put(struct mxr_device *mdev)
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);
136 int mxr_power_get(struct mxr_device *mdev)
138 int ret = pm_runtime_get_sync(mdev->dev);
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;
147 void mxr_power_put(struct mxr_device *mdev)
149 pm_runtime_put_sync(mdev->dev);
152 /* --------- RESOURCE MANAGEMENT -------------*/
154 static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
155 struct platform_device *pdev)
157 struct resource *res;
158 int ret;
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;
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;
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;
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;
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;
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;
200 mdev->res.irq = res->start;
202 return 0;
204 fail_vp_regs:
205 iounmap(mdev->res.vp_regs);
207 fail_mxr_regs:
208 iounmap(mdev->res.mxr_regs);
210 fail:
211 return ret;
214 static void mxr_release_plat_resources(struct mxr_device *mdev)
216 free_irq(mdev->res.irq, mdev);
217 iounmap(mdev->res.vp_regs);
218 iounmap(mdev->res.mxr_regs);
221 static void mxr_release_clocks(struct mxr_device *mdev)
223 struct mxr_resources *res = &mdev->res;
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);
237 static int mxr_acquire_clocks(struct mxr_device *mdev)
239 struct mxr_resources *res = &mdev->res;
240 struct device *dev = mdev->dev;
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;
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;
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;
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;
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;
268 return 0;
269 fail:
270 mxr_release_clocks(mdev);
271 return -ENODEV;
274 static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
275 struct platform_device *pdev)
277 int ret;
278 ret = mxr_acquire_plat_resources(mdev, pdev);
280 if (ret)
281 goto fail;
283 ret = mxr_acquire_clocks(mdev);
284 if (ret)
285 goto fail_plat;
287 mxr_info(mdev, "resources acquired\n");
288 return 0;
290 fail_plat:
291 mxr_release_plat_resources(mdev);
292 fail:
293 mxr_err(mdev, "resources acquire failed\n");
294 return ret;
297 static void mxr_release_resources(struct mxr_device *mdev)
299 mxr_release_clocks(mdev);
300 mxr_release_plat_resources(mdev);
301 memset(&mdev->res, 0, sizeof mdev->res);
304 static void mxr_release_layers(struct mxr_device *mdev)
306 int i;
308 for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
309 if (mdev->layer[i])
310 mxr_layer_release(mdev->layer[i]);
313 static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
314 struct mxr_platform_data *pdata)
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);
320 if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
321 mxr_err(mdev, "failed to acquire layers\n");
322 goto fail;
325 return 0;
327 fail:
328 mxr_release_layers(mdev);
329 return -ENODEV;
332 /* ---------- POWER MANAGEMENT ----------- */
334 static int mxr_runtime_resume(struct device *dev)
336 struct mxr_device *mdev = to_mdev(dev);
337 struct mxr_resources *res = &mdev->res;
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");
349 mutex_unlock(&mdev->mutex);
350 return 0;
353 static int mxr_runtime_suspend(struct device *dev)
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;
368 static const struct dev_pm_ops mxr_pm_ops = {
369 .runtime_suspend = mxr_runtime_suspend,
370 .runtime_resume = mxr_runtime_resume,
373 /* --------- DRIVER INITIALIZATION ---------- */
375 static int __devinit mxr_probe(struct platform_device *pdev)
377 struct device *dev = &pdev->dev;
378 struct mxr_platform_data *pdata = dev->platform_data;
379 struct mxr_device *mdev;
380 int ret;
382 /* mdev does not exist yet so no mxr_dbg is used */
383 dev_info(dev, "probe start\n");
385 mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
386 if (!mdev) {
387 mxr_err(mdev, "not enough memory.\n");
388 ret = -ENOMEM;
389 goto fail;
392 /* setup pointer to master device */
393 mdev->dev = dev;
395 mutex_init(&mdev->mutex);
396 spin_lock_init(&mdev->reg_slock);
397 init_waitqueue_head(&mdev->event_queue);
399 /* acquire resources: regs, irqs, clocks, regulators */
400 ret = mxr_acquire_resources(mdev, pdev);
401 if (ret)
402 goto fail_mem;
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;
410 /* configure layers */
411 ret = mxr_acquire_layers(mdev, pdata);
412 if (ret)
413 goto fail_video;
415 pm_runtime_enable(dev);
417 mxr_info(mdev, "probe successful\n");
418 return 0;
420 fail_video:
421 mxr_release_video(mdev);
423 fail_resources:
424 mxr_release_resources(mdev);
426 fail_mem:
427 kfree(mdev);
429 fail:
430 dev_info(dev, "probe failed\n");
431 return ret;
434 static int __devexit mxr_remove(struct platform_device *pdev)
436 struct device *dev = &pdev->dev;
437 struct mxr_device *mdev = to_mdev(dev);
439 pm_runtime_disable(dev);
441 mxr_release_layers(mdev);
442 mxr_release_video(mdev);
443 mxr_release_resources(mdev);
445 kfree(mdev);
447 dev_info(dev, "remove sucessful\n");
448 return 0;
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,
461 static int __init mxr_init(void)
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);
469 /* Loading auxiliary modules */
470 for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
471 request_module(mxr_output_conf[i].module_name);
473 ret = platform_driver_register(&mxr_driver);
474 if (ret != 0) {
475 printk(KERN_ERR "registration of MIXER driver failed\n");
476 return -ENXIO;
479 return 0;
481 module_init(mxr_init);
483 static void __exit mxr_exit(void)
485 platform_driver_unregister(&mxr_driver);
487 module_exit(mxr_exit);