blob: bc08b5f28e447795b4c7d61526666ca551194e26 [file] [log] [blame]
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -03001/*
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;
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
58void 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
90void 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
112void 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
123void 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
136int 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
147void mxr_power_put(struct mxr_device *mdev)
148{
149 pm_runtime_put_sync(mdev->dev);
150}
151
152/* --------- RESOURCE MANAGEMENT -------------*/
153
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800154static int mxr_acquire_plat_resources(struct mxr_device *mdev,
155 struct platform_device *pdev)
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300156{
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
204fail_vp_regs:
205 iounmap(mdev->res.vp_regs);
206
207fail_mxr_regs:
208 iounmap(mdev->res.mxr_regs);
209
210fail:
211 return ret;
212}
213
Sachin Kamat0781c052013-05-17 00:31:02 -0300214static void mxr_resource_clear_clocks(struct mxr_resources *res)
215{
216 res->mixer = ERR_PTR(-EINVAL);
217 res->vp = ERR_PTR(-EINVAL);
218 res->sclk_mixer = ERR_PTR(-EINVAL);
219 res->sclk_hdmi = ERR_PTR(-EINVAL);
220 res->sclk_dac = ERR_PTR(-EINVAL);
221}
222
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300223static void mxr_release_plat_resources(struct mxr_device *mdev)
224{
225 free_irq(mdev->res.irq, mdev);
226 iounmap(mdev->res.vp_regs);
227 iounmap(mdev->res.mxr_regs);
228}
229
230static void mxr_release_clocks(struct mxr_device *mdev)
231{
232 struct mxr_resources *res = &mdev->res;
233
Sachin Kamat0781c052013-05-17 00:31:02 -0300234 if (!IS_ERR(res->sclk_dac))
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300235 clk_put(res->sclk_dac);
Sachin Kamat0781c052013-05-17 00:31:02 -0300236 if (!IS_ERR(res->sclk_hdmi))
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300237 clk_put(res->sclk_hdmi);
Sachin Kamat0781c052013-05-17 00:31:02 -0300238 if (!IS_ERR(res->sclk_mixer))
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300239 clk_put(res->sclk_mixer);
Sachin Kamat0781c052013-05-17 00:31:02 -0300240 if (!IS_ERR(res->vp))
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300241 clk_put(res->vp);
Sachin Kamat0781c052013-05-17 00:31:02 -0300242 if (!IS_ERR(res->mixer))
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300243 clk_put(res->mixer);
244}
245
246static int mxr_acquire_clocks(struct mxr_device *mdev)
247{
248 struct mxr_resources *res = &mdev->res;
249 struct device *dev = mdev->dev;
250
Sachin Kamat0781c052013-05-17 00:31:02 -0300251 mxr_resource_clear_clocks(res);
252
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300253 res->mixer = clk_get(dev, "mixer");
Tony Priskaeae3db2012-12-18 04:28:39 -0300254 if (IS_ERR(res->mixer)) {
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300255 mxr_err(mdev, "failed to get clock 'mixer'\n");
256 goto fail;
257 }
258 res->vp = clk_get(dev, "vp");
Tony Priskaeae3db2012-12-18 04:28:39 -0300259 if (IS_ERR(res->vp)) {
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300260 mxr_err(mdev, "failed to get clock 'vp'\n");
261 goto fail;
262 }
263 res->sclk_mixer = clk_get(dev, "sclk_mixer");
Tony Priskaeae3db2012-12-18 04:28:39 -0300264 if (IS_ERR(res->sclk_mixer)) {
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300265 mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
266 goto fail;
267 }
268 res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
Tony Priskaeae3db2012-12-18 04:28:39 -0300269 if (IS_ERR(res->sclk_hdmi)) {
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300270 mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
271 goto fail;
272 }
273 res->sclk_dac = clk_get(dev, "sclk_dac");
Tony Priskaeae3db2012-12-18 04:28:39 -0300274 if (IS_ERR(res->sclk_dac)) {
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300275 mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
276 goto fail;
277 }
278
279 return 0;
280fail:
281 mxr_release_clocks(mdev);
282 return -ENODEV;
283}
284
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800285static int mxr_acquire_resources(struct mxr_device *mdev,
286 struct platform_device *pdev)
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300287{
288 int ret;
289 ret = mxr_acquire_plat_resources(mdev, pdev);
290
291 if (ret)
292 goto fail;
293
294 ret = mxr_acquire_clocks(mdev);
295 if (ret)
296 goto fail_plat;
297
298 mxr_info(mdev, "resources acquired\n");
299 return 0;
300
301fail_plat:
302 mxr_release_plat_resources(mdev);
303fail:
304 mxr_err(mdev, "resources acquire failed\n");
305 return ret;
306}
307
308static void mxr_release_resources(struct mxr_device *mdev)
309{
310 mxr_release_clocks(mdev);
311 mxr_release_plat_resources(mdev);
Sachin Kamatdc033982012-11-26 01:49:03 -0300312 memset(&mdev->res, 0, sizeof(mdev->res));
Sachin Kamat0781c052013-05-17 00:31:02 -0300313 mxr_resource_clear_clocks(&mdev->res);
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300314}
315
316static void mxr_release_layers(struct mxr_device *mdev)
317{
318 int i;
319
320 for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
321 if (mdev->layer[i])
322 mxr_layer_release(mdev->layer[i]);
323}
324
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800325static int mxr_acquire_layers(struct mxr_device *mdev,
326 struct mxr_platform_data *pdata)
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300327{
328 mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
329 mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
330 mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
331
332 if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
333 mxr_err(mdev, "failed to acquire layers\n");
334 goto fail;
335 }
336
337 return 0;
338
339fail:
340 mxr_release_layers(mdev);
341 return -ENODEV;
342}
343
344/* ---------- POWER MANAGEMENT ----------- */
345
346static int mxr_runtime_resume(struct device *dev)
347{
348 struct mxr_device *mdev = to_mdev(dev);
349 struct mxr_resources *res = &mdev->res;
Mateusz Krawczuk03ce1a12013-09-21 11:00:49 -0300350 int ret;
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300351
352 mxr_dbg(mdev, "resume - start\n");
353 mutex_lock(&mdev->mutex);
354 /* turn clocks on */
Mateusz Krawczuk03ce1a12013-09-21 11:00:49 -0300355 ret = clk_prepare_enable(res->mixer);
356 if (ret < 0) {
357 dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n");
358 goto fail;
359 }
360 ret = clk_prepare_enable(res->vp);
361 if (ret < 0) {
362 dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n");
363 goto fail_mixer;
364 }
365 ret = clk_prepare_enable(res->sclk_mixer);
366 if (ret < 0) {
367 dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n");
368 goto fail_vp;
369 }
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300370 /* apply default configuration */
371 mxr_reg_reset(mdev);
372 mxr_dbg(mdev, "resume - finished\n");
373
374 mutex_unlock(&mdev->mutex);
375 return 0;
Mateusz Krawczuk03ce1a12013-09-21 11:00:49 -0300376
377fail_vp:
378 clk_disable_unprepare(res->vp);
379fail_mixer:
380 clk_disable_unprepare(res->mixer);
381fail:
382 mutex_unlock(&mdev->mutex);
383 dev_err(mdev->dev, "resume failed\n");
384 return ret;
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300385}
386
387static int mxr_runtime_suspend(struct device *dev)
388{
389 struct mxr_device *mdev = to_mdev(dev);
390 struct mxr_resources *res = &mdev->res;
391 mxr_dbg(mdev, "suspend - start\n");
392 mutex_lock(&mdev->mutex);
393 /* turn clocks off */
Mateusz Krawczuk03ce1a12013-09-21 11:00:49 -0300394 clk_disable_unprepare(res->sclk_mixer);
395 clk_disable_unprepare(res->vp);
396 clk_disable_unprepare(res->mixer);
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300397 mutex_unlock(&mdev->mutex);
398 mxr_dbg(mdev, "suspend - finished\n");
399 return 0;
400}
401
402static const struct dev_pm_ops mxr_pm_ops = {
403 .runtime_suspend = mxr_runtime_suspend,
404 .runtime_resume = mxr_runtime_resume,
405};
406
407/* --------- DRIVER INITIALIZATION ---------- */
408
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800409static int mxr_probe(struct platform_device *pdev)
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300410{
411 struct device *dev = &pdev->dev;
412 struct mxr_platform_data *pdata = dev->platform_data;
413 struct mxr_device *mdev;
414 int ret;
415
416 /* mdev does not exist yet so no mxr_dbg is used */
417 dev_info(dev, "probe start\n");
418
Sachin Kamatdc033982012-11-26 01:49:03 -0300419 mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300420 if (!mdev) {
Sachin Kamat970e85f2012-09-22 03:39:19 -0300421 dev_err(dev, "not enough memory.\n");
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300422 ret = -ENOMEM;
423 goto fail;
424 }
425
426 /* setup pointer to master device */
427 mdev->dev = dev;
428
429 mutex_init(&mdev->mutex);
430 spin_lock_init(&mdev->reg_slock);
431 init_waitqueue_head(&mdev->event_queue);
432
433 /* acquire resources: regs, irqs, clocks, regulators */
434 ret = mxr_acquire_resources(mdev, pdev);
435 if (ret)
436 goto fail_mem;
437
438 /* configure resources for video output */
439 ret = mxr_acquire_video(mdev, mxr_output_conf,
440 ARRAY_SIZE(mxr_output_conf));
441 if (ret)
442 goto fail_resources;
443
444 /* configure layers */
445 ret = mxr_acquire_layers(mdev, pdata);
446 if (ret)
447 goto fail_video;
448
449 pm_runtime_enable(dev);
450
451 mxr_info(mdev, "probe successful\n");
452 return 0;
453
454fail_video:
455 mxr_release_video(mdev);
456
457fail_resources:
458 mxr_release_resources(mdev);
459
460fail_mem:
461 kfree(mdev);
462
463fail:
464 dev_info(dev, "probe failed\n");
465 return ret;
466}
467
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800468static int mxr_remove(struct platform_device *pdev)
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300469{
470 struct device *dev = &pdev->dev;
471 struct mxr_device *mdev = to_mdev(dev);
472
473 pm_runtime_disable(dev);
474
475 mxr_release_layers(mdev);
476 mxr_release_video(mdev);
477 mxr_release_resources(mdev);
478
479 kfree(mdev);
480
Masanari Iida41292b162012-01-29 08:50:53 -0300481 dev_info(dev, "remove successful\n");
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300482 return 0;
483}
484
485static struct platform_driver mxr_driver __refdata = {
486 .probe = mxr_probe,
Greg Kroah-Hartman4c62e972012-12-21 13:17:53 -0800487 .remove = mxr_remove,
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300488 .driver = {
489 .name = MXR_DRIVER_NAME,
490 .owner = THIS_MODULE,
491 .pm = &mxr_pm_ops,
492 }
493};
494
495static int __init mxr_init(void)
496{
497 int i, ret;
Sachin Kamatf5c99032012-06-11 06:13:52 -0300498 static const char banner[] __initconst =
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300499 "Samsung TV Mixer driver, "
500 "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
Sachin Kamatf5c99032012-06-11 06:13:52 -0300501 pr_info("%s\n", banner);
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300502
503 /* Loading auxiliary modules */
504 for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
505 request_module(mxr_output_conf[i].module_name);
506
507 ret = platform_driver_register(&mxr_driver);
508 if (ret != 0) {
Sachin Kamatf5c99032012-06-11 06:13:52 -0300509 pr_err("s5p-tv: registration of MIXER driver failed\n");
Tomasz Stanislawskifef1c8d2011-02-02 05:40:08 -0300510 return -ENXIO;
511 }
512
513 return 0;
514}
515module_init(mxr_init);
516
517static void __exit mxr_exit(void)
518{
519 platform_driver_unregister(&mxr_driver);
520}
521module_exit(mxr_exit);