Merge branch 'stable-4.7' of git://git.infradead.org/users/pcmoore/audit
[deliverable/linux.git] / drivers / media / platform / s5p-tv / mixer_drv.c
CommitLineData
fef1c8d0
TS
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
26MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
27MODULE_DESCRIPTION("Samsung MIXER");
28MODULE_LICENSE("GPL");
29
30/* --------- DRIVER PARAMETERS ---------- */
31
32static 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
45void mxr_get_mbus_fmt(struct mxr_device *mdev,
46 struct v4l2_mbus_framefmt *mbus_fmt)
47{
48 struct v4l2_subdev *sd;
da298c6d
HV
49 struct v4l2_subdev_format fmt = {
50 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
51 };
fef1c8d0
TS
52 int ret;
53
54 mutex_lock(&mdev->mutex);
55 sd = to_outsd(mdev);
da298c6d
HV
56 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
57 *mbus_fmt = fmt.format;
fef1c8d0
TS
58 WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
59 mutex_unlock(&mdev->mutex);
60}
61
62void mxr_streamer_get(struct mxr_device *mdev)
63{
64 mutex_lock(&mdev->mutex);
65 ++mdev->n_streamer;
66 mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
67 if (mdev->n_streamer == 1) {
68 struct v4l2_subdev *sd = to_outsd(mdev);
da298c6d
HV
69 struct v4l2_subdev_format fmt = {
70 .which = V4L2_SUBDEV_FORMAT_ACTIVE,
71 };
72 struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format;
fef1c8d0
TS
73 struct mxr_resources *res = &mdev->res;
74 int ret;
75
76 if (to_output(mdev)->cookie == 0)
77 clk_set_parent(res->sclk_mixer, res->sclk_dac);
78 else
79 clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
80 mxr_reg_s_output(mdev, to_output(mdev)->cookie);
81
da298c6d 82 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
fef1c8d0
TS
83 WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
84 ret = v4l2_subdev_call(sd, video, s_stream, 1);
85 WARN(ret, "starting stream failed for output %s\n", sd->name);
86
da298c6d 87 mxr_reg_set_mbus_fmt(mdev, mbus_fmt);
fef1c8d0
TS
88 mxr_reg_streamon(mdev);
89 ret = mxr_reg_wait4vsync(mdev);
90 WARN(ret, "failed to get vsync (%d) from output\n", ret);
91 }
92 mutex_unlock(&mdev->mutex);
93 mxr_reg_dump(mdev);
94 /* FIXME: what to do when streaming fails? */
95}
96
97void mxr_streamer_put(struct mxr_device *mdev)
98{
99 mutex_lock(&mdev->mutex);
100 --mdev->n_streamer;
101 mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
102 if (mdev->n_streamer == 0) {
103 int ret;
104 struct v4l2_subdev *sd = to_outsd(mdev);
105
106 mxr_reg_streamoff(mdev);
107 /* vsync applies Mixer setup */
108 ret = mxr_reg_wait4vsync(mdev);
109 WARN(ret, "failed to get vsync (%d) from output\n", ret);
110 ret = v4l2_subdev_call(sd, video, s_stream, 0);
111 WARN(ret, "stopping stream failed for output %s\n", sd->name);
112 }
113 WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
114 mdev->n_streamer);
115 mutex_unlock(&mdev->mutex);
116 mxr_reg_dump(mdev);
117}
118
119void mxr_output_get(struct mxr_device *mdev)
120{
121 mutex_lock(&mdev->mutex);
122 ++mdev->n_output;
123 mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
124 /* turn on auxiliary driver */
125 if (mdev->n_output == 1)
126 v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
127 mutex_unlock(&mdev->mutex);
128}
129
130void mxr_output_put(struct mxr_device *mdev)
131{
132 mutex_lock(&mdev->mutex);
133 --mdev->n_output;
134 mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
135 /* turn on auxiliary driver */
136 if (mdev->n_output == 0)
137 v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
138 WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
139 mdev->n_output);
140 mutex_unlock(&mdev->mutex);
141}
142
143int mxr_power_get(struct mxr_device *mdev)
144{
145 int ret = pm_runtime_get_sync(mdev->dev);
146
147 /* returning 1 means that power is already enabled,
148 * so zero success be returned */
287980e4 149 if (ret < 0)
fef1c8d0
TS
150 return ret;
151 return 0;
152}
153
154void mxr_power_put(struct mxr_device *mdev)
155{
156 pm_runtime_put_sync(mdev->dev);
157}
158
159/* --------- RESOURCE MANAGEMENT -------------*/
160
4c62e976
GKH
161static int mxr_acquire_plat_resources(struct mxr_device *mdev,
162 struct platform_device *pdev)
fef1c8d0
TS
163{
164 struct resource *res;
165 int ret;
166
167 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
168 if (res == NULL) {
169 mxr_err(mdev, "get memory resource failed.\n");
170 ret = -ENXIO;
171 goto fail;
172 }
173
174 mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
175 if (mdev->res.mxr_regs == NULL) {
176 mxr_err(mdev, "register mapping failed.\n");
177 ret = -ENXIO;
178 goto fail;
179 }
180
181 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
182 if (res == NULL) {
183 mxr_err(mdev, "get memory resource failed.\n");
184 ret = -ENXIO;
185 goto fail_mxr_regs;
186 }
187
188 mdev->res.vp_regs = ioremap(res->start, resource_size(res));
189 if (mdev->res.vp_regs == NULL) {
190 mxr_err(mdev, "register mapping failed.\n");
191 ret = -ENXIO;
192 goto fail_mxr_regs;
193 }
194
195 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
196 if (res == NULL) {
197 mxr_err(mdev, "get interrupt resource failed.\n");
198 ret = -ENXIO;
199 goto fail_vp_regs;
200 }
201
202 ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
203 if (ret) {
204 mxr_err(mdev, "request interrupt failed.\n");
205 goto fail_vp_regs;
206 }
207 mdev->res.irq = res->start;
208
209 return 0;
210
211fail_vp_regs:
212 iounmap(mdev->res.vp_regs);
213
214fail_mxr_regs:
215 iounmap(mdev->res.mxr_regs);
216
217fail:
218 return ret;
219}
220
0781c057
SK
221static void mxr_resource_clear_clocks(struct mxr_resources *res)
222{
223 res->mixer = ERR_PTR(-EINVAL);
224 res->vp = ERR_PTR(-EINVAL);
225 res->sclk_mixer = ERR_PTR(-EINVAL);
226 res->sclk_hdmi = ERR_PTR(-EINVAL);
227 res->sclk_dac = ERR_PTR(-EINVAL);
228}
229
fef1c8d0
TS
230static void mxr_release_plat_resources(struct mxr_device *mdev)
231{
232 free_irq(mdev->res.irq, mdev);
233 iounmap(mdev->res.vp_regs);
234 iounmap(mdev->res.mxr_regs);
235}
236
237static void mxr_release_clocks(struct mxr_device *mdev)
238{
239 struct mxr_resources *res = &mdev->res;
240
0781c057 241 if (!IS_ERR(res->sclk_dac))
fef1c8d0 242 clk_put(res->sclk_dac);
0781c057 243 if (!IS_ERR(res->sclk_hdmi))
fef1c8d0 244 clk_put(res->sclk_hdmi);
0781c057 245 if (!IS_ERR(res->sclk_mixer))
fef1c8d0 246 clk_put(res->sclk_mixer);
0781c057 247 if (!IS_ERR(res->vp))
fef1c8d0 248 clk_put(res->vp);
0781c057 249 if (!IS_ERR(res->mixer))
fef1c8d0
TS
250 clk_put(res->mixer);
251}
252
253static int mxr_acquire_clocks(struct mxr_device *mdev)
254{
255 struct mxr_resources *res = &mdev->res;
256 struct device *dev = mdev->dev;
257
0781c057
SK
258 mxr_resource_clear_clocks(res);
259
fef1c8d0 260 res->mixer = clk_get(dev, "mixer");
aeae3db1 261 if (IS_ERR(res->mixer)) {
fef1c8d0
TS
262 mxr_err(mdev, "failed to get clock 'mixer'\n");
263 goto fail;
264 }
265 res->vp = clk_get(dev, "vp");
aeae3db1 266 if (IS_ERR(res->vp)) {
fef1c8d0
TS
267 mxr_err(mdev, "failed to get clock 'vp'\n");
268 goto fail;
269 }
270 res->sclk_mixer = clk_get(dev, "sclk_mixer");
aeae3db1 271 if (IS_ERR(res->sclk_mixer)) {
fef1c8d0
TS
272 mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
273 goto fail;
274 }
275 res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
aeae3db1 276 if (IS_ERR(res->sclk_hdmi)) {
fef1c8d0
TS
277 mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
278 goto fail;
279 }
280 res->sclk_dac = clk_get(dev, "sclk_dac");
aeae3db1 281 if (IS_ERR(res->sclk_dac)) {
fef1c8d0
TS
282 mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
283 goto fail;
284 }
285
286 return 0;
287fail:
288 mxr_release_clocks(mdev);
289 return -ENODEV;
290}
291
4c62e976
GKH
292static int mxr_acquire_resources(struct mxr_device *mdev,
293 struct platform_device *pdev)
fef1c8d0
TS
294{
295 int ret;
296 ret = mxr_acquire_plat_resources(mdev, pdev);
297
298 if (ret)
299 goto fail;
300
301 ret = mxr_acquire_clocks(mdev);
302 if (ret)
303 goto fail_plat;
304
305 mxr_info(mdev, "resources acquired\n");
306 return 0;
307
308fail_plat:
309 mxr_release_plat_resources(mdev);
310fail:
311 mxr_err(mdev, "resources acquire failed\n");
312 return ret;
313}
314
315static void mxr_release_resources(struct mxr_device *mdev)
316{
317 mxr_release_clocks(mdev);
318 mxr_release_plat_resources(mdev);
dc033985 319 memset(&mdev->res, 0, sizeof(mdev->res));
0781c057 320 mxr_resource_clear_clocks(&mdev->res);
fef1c8d0
TS
321}
322
323static void mxr_release_layers(struct mxr_device *mdev)
324{
325 int i;
326
327 for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
328 if (mdev->layer[i])
329 mxr_layer_release(mdev->layer[i]);
330}
331
4c62e976
GKH
332static int mxr_acquire_layers(struct mxr_device *mdev,
333 struct mxr_platform_data *pdata)
fef1c8d0
TS
334{
335 mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
336 mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
337 mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
338
339 if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
340 mxr_err(mdev, "failed to acquire layers\n");
341 goto fail;
342 }
343
344 return 0;
345
346fail:
347 mxr_release_layers(mdev);
348 return -ENODEV;
349}
350
351/* ---------- POWER MANAGEMENT ----------- */
352
353static int mxr_runtime_resume(struct device *dev)
354{
355 struct mxr_device *mdev = to_mdev(dev);
356 struct mxr_resources *res = &mdev->res;
03ce1a13 357 int ret;
fef1c8d0
TS
358
359 mxr_dbg(mdev, "resume - start\n");
360 mutex_lock(&mdev->mutex);
361 /* turn clocks on */
03ce1a13
MK
362 ret = clk_prepare_enable(res->mixer);
363 if (ret < 0) {
364 dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n");
365 goto fail;
366 }
367 ret = clk_prepare_enable(res->vp);
368 if (ret < 0) {
369 dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n");
370 goto fail_mixer;
371 }
372 ret = clk_prepare_enable(res->sclk_mixer);
373 if (ret < 0) {
374 dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n");
375 goto fail_vp;
376 }
fef1c8d0
TS
377 /* apply default configuration */
378 mxr_reg_reset(mdev);
379 mxr_dbg(mdev, "resume - finished\n");
380
381 mutex_unlock(&mdev->mutex);
382 return 0;
03ce1a13
MK
383
384fail_vp:
385 clk_disable_unprepare(res->vp);
386fail_mixer:
387 clk_disable_unprepare(res->mixer);
388fail:
389 mutex_unlock(&mdev->mutex);
390 dev_err(mdev->dev, "resume failed\n");
391 return ret;
fef1c8d0
TS
392}
393
394static int mxr_runtime_suspend(struct device *dev)
395{
396 struct mxr_device *mdev = to_mdev(dev);
397 struct mxr_resources *res = &mdev->res;
398 mxr_dbg(mdev, "suspend - start\n");
399 mutex_lock(&mdev->mutex);
400 /* turn clocks off */
03ce1a13
MK
401 clk_disable_unprepare(res->sclk_mixer);
402 clk_disable_unprepare(res->vp);
403 clk_disable_unprepare(res->mixer);
fef1c8d0
TS
404 mutex_unlock(&mdev->mutex);
405 mxr_dbg(mdev, "suspend - finished\n");
406 return 0;
407}
408
409static const struct dev_pm_ops mxr_pm_ops = {
410 .runtime_suspend = mxr_runtime_suspend,
411 .runtime_resume = mxr_runtime_resume,
412};
413
414/* --------- DRIVER INITIALIZATION ---------- */
415
4c62e976 416static int mxr_probe(struct platform_device *pdev)
fef1c8d0
TS
417{
418 struct device *dev = &pdev->dev;
419 struct mxr_platform_data *pdata = dev->platform_data;
420 struct mxr_device *mdev;
421 int ret;
422
423 /* mdev does not exist yet so no mxr_dbg is used */
424 dev_info(dev, "probe start\n");
425
dc033985 426 mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
fef1c8d0 427 if (!mdev) {
970e85f8 428 dev_err(dev, "not enough memory.\n");
fef1c8d0
TS
429 ret = -ENOMEM;
430 goto fail;
431 }
432
433 /* setup pointer to master device */
434 mdev->dev = dev;
435
436 mutex_init(&mdev->mutex);
437 spin_lock_init(&mdev->reg_slock);
438 init_waitqueue_head(&mdev->event_queue);
439
440 /* acquire resources: regs, irqs, clocks, regulators */
441 ret = mxr_acquire_resources(mdev, pdev);
442 if (ret)
443 goto fail_mem;
444
445 /* configure resources for video output */
446 ret = mxr_acquire_video(mdev, mxr_output_conf,
447 ARRAY_SIZE(mxr_output_conf));
448 if (ret)
449 goto fail_resources;
450
451 /* configure layers */
452 ret = mxr_acquire_layers(mdev, pdata);
453 if (ret)
454 goto fail_video;
455
456 pm_runtime_enable(dev);
457
458 mxr_info(mdev, "probe successful\n");
459 return 0;
460
461fail_video:
462 mxr_release_video(mdev);
463
464fail_resources:
465 mxr_release_resources(mdev);
466
467fail_mem:
468 kfree(mdev);
469
470fail:
471 dev_info(dev, "probe failed\n");
472 return ret;
473}
474
4c62e976 475static int mxr_remove(struct platform_device *pdev)
fef1c8d0
TS
476{
477 struct device *dev = &pdev->dev;
478 struct mxr_device *mdev = to_mdev(dev);
479
480 pm_runtime_disable(dev);
481
482 mxr_release_layers(mdev);
483 mxr_release_video(mdev);
484 mxr_release_resources(mdev);
485
486 kfree(mdev);
487
41292b16 488 dev_info(dev, "remove successful\n");
fef1c8d0
TS
489 return 0;
490}
491
492static struct platform_driver mxr_driver __refdata = {
493 .probe = mxr_probe,
4c62e976 494 .remove = mxr_remove,
fef1c8d0
TS
495 .driver = {
496 .name = MXR_DRIVER_NAME,
fef1c8d0
TS
497 .pm = &mxr_pm_ops,
498 }
499};
500
501static int __init mxr_init(void)
502{
503 int i, ret;
f5c99037 504 static const char banner[] __initconst =
fef1c8d0
TS
505 "Samsung TV Mixer driver, "
506 "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
f5c99037 507 pr_info("%s\n", banner);
fef1c8d0
TS
508
509 /* Loading auxiliary modules */
510 for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
511 request_module(mxr_output_conf[i].module_name);
512
513 ret = platform_driver_register(&mxr_driver);
514 if (ret != 0) {
f5c99037 515 pr_err("s5p-tv: registration of MIXER driver failed\n");
fef1c8d0
TS
516 return -ENXIO;
517 }
518
519 return 0;
520}
521module_init(mxr_init);
522
523static void __exit mxr_exit(void)
524{
525 platform_driver_unregister(&mxr_driver);
526}
527module_exit(mxr_exit);
This page took 0.464031 seconds and 5 git commands to generate.