123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- /*
- * Samsung TV Mixer driver
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *
- * Tomasz Stanislawski, <t.stanislaws@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundiation. either version 2 of the License,
- * or (at your option) any later version
- */
- #include "mixer.h"
- #include <linux/module.h>
- #include <linux/platform_device.h>
- #include <linux/io.h>
- #include <linux/interrupt.h>
- #include <linux/irq.h>
- #include <linux/fb.h>
- #include <linux/delay.h>
- #include <linux/pm_runtime.h>
- #include <linux/clk.h>
- MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
- MODULE_DESCRIPTION("Samsung MIXER");
- MODULE_LICENSE("GPL");
- /* --------- DRIVER PARAMETERS ---------- */
- static struct mxr_output_conf mxr_output_conf[] = {
- {
- .output_name = "S5P HDMI connector",
- .module_name = "s5p-hdmi",
- .cookie = 1,
- },
- {
- .output_name = "S5P SDO connector",
- .module_name = "s5p-sdo",
- .cookie = 0,
- },
- };
- void mxr_get_mbus_fmt(struct mxr_device *mdev,
- struct v4l2_mbus_framefmt *mbus_fmt)
- {
- struct v4l2_subdev *sd;
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
- mutex_lock(&mdev->mutex);
- sd = to_outsd(mdev);
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- *mbus_fmt = fmt.format;
- WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
- mutex_unlock(&mdev->mutex);
- }
- void mxr_streamer_get(struct mxr_device *mdev)
- {
- mutex_lock(&mdev->mutex);
- ++mdev->n_streamer;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
- if (mdev->n_streamer == 1) {
- struct v4l2_subdev *sd = to_outsd(mdev);
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format;
- struct mxr_resources *res = &mdev->res;
- int ret;
- if (to_output(mdev)->cookie == 0)
- clk_set_parent(res->sclk_mixer, res->sclk_dac);
- else
- clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
- mxr_reg_s_output(mdev, to_output(mdev)->cookie);
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
- WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
- WARN(ret, "starting stream failed for output %s\n", sd->name);
- mxr_reg_set_mbus_fmt(mdev, mbus_fmt);
- mxr_reg_streamon(mdev);
- ret = mxr_reg_wait4vsync(mdev);
- WARN(ret, "failed to get vsync (%d) from output\n", ret);
- }
- mutex_unlock(&mdev->mutex);
- mxr_reg_dump(mdev);
- /* FIXME: what to do when streaming fails? */
- }
- void mxr_streamer_put(struct mxr_device *mdev)
- {
- mutex_lock(&mdev->mutex);
- --mdev->n_streamer;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
- if (mdev->n_streamer == 0) {
- int ret;
- struct v4l2_subdev *sd = to_outsd(mdev);
- mxr_reg_streamoff(mdev);
- /* vsync applies Mixer setup */
- ret = mxr_reg_wait4vsync(mdev);
- WARN(ret, "failed to get vsync (%d) from output\n", ret);
- ret = v4l2_subdev_call(sd, video, s_stream, 0);
- WARN(ret, "stopping stream failed for output %s\n", sd->name);
- }
- WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
- mdev->n_streamer);
- mutex_unlock(&mdev->mutex);
- mxr_reg_dump(mdev);
- }
- void mxr_output_get(struct mxr_device *mdev)
- {
- mutex_lock(&mdev->mutex);
- ++mdev->n_output;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
- /* turn on auxiliary driver */
- if (mdev->n_output == 1)
- v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
- mutex_unlock(&mdev->mutex);
- }
- void mxr_output_put(struct mxr_device *mdev)
- {
- mutex_lock(&mdev->mutex);
- --mdev->n_output;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
- /* turn on auxiliary driver */
- if (mdev->n_output == 0)
- v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
- WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
- mdev->n_output);
- mutex_unlock(&mdev->mutex);
- }
- int mxr_power_get(struct mxr_device *mdev)
- {
- int ret = pm_runtime_get_sync(mdev->dev);
- /* returning 1 means that power is already enabled,
- * so zero success be returned */
- if (IS_ERR_VALUE(ret))
- return ret;
- return 0;
- }
- void mxr_power_put(struct mxr_device *mdev)
- {
- pm_runtime_put_sync(mdev->dev);
- }
- /* --------- RESOURCE MANAGEMENT -------------*/
- static int mxr_acquire_plat_resources(struct mxr_device *mdev,
- struct platform_device *pdev)
- {
- struct resource *res;
- int ret;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
- if (res == NULL) {
- mxr_err(mdev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail;
- }
- mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
- if (mdev->res.mxr_regs == NULL) {
- mxr_err(mdev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail;
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
- if (res == NULL) {
- mxr_err(mdev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail_mxr_regs;
- }
- mdev->res.vp_regs = ioremap(res->start, resource_size(res));
- if (mdev->res.vp_regs == NULL) {
- mxr_err(mdev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail_mxr_regs;
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
- if (res == NULL) {
- mxr_err(mdev, "get interrupt resource failed.\n");
- ret = -ENXIO;
- goto fail_vp_regs;
- }
- ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
- if (ret) {
- mxr_err(mdev, "request interrupt failed.\n");
- goto fail_vp_regs;
- }
- mdev->res.irq = res->start;
- return 0;
- fail_vp_regs:
- iounmap(mdev->res.vp_regs);
- fail_mxr_regs:
- iounmap(mdev->res.mxr_regs);
- fail:
- return ret;
- }
- static void mxr_resource_clear_clocks(struct mxr_resources *res)
- {
- res->mixer = ERR_PTR(-EINVAL);
- res->vp = ERR_PTR(-EINVAL);
- res->sclk_mixer = ERR_PTR(-EINVAL);
- res->sclk_hdmi = ERR_PTR(-EINVAL);
- res->sclk_dac = ERR_PTR(-EINVAL);
- }
- static void mxr_release_plat_resources(struct mxr_device *mdev)
- {
- free_irq(mdev->res.irq, mdev);
- iounmap(mdev->res.vp_regs);
- iounmap(mdev->res.mxr_regs);
- }
- static void mxr_release_clocks(struct mxr_device *mdev)
- {
- struct mxr_resources *res = &mdev->res;
- if (!IS_ERR(res->sclk_dac))
- clk_put(res->sclk_dac);
- if (!IS_ERR(res->sclk_hdmi))
- clk_put(res->sclk_hdmi);
- if (!IS_ERR(res->sclk_mixer))
- clk_put(res->sclk_mixer);
- if (!IS_ERR(res->vp))
- clk_put(res->vp);
- if (!IS_ERR(res->mixer))
- clk_put(res->mixer);
- }
- static int mxr_acquire_clocks(struct mxr_device *mdev)
- {
- struct mxr_resources *res = &mdev->res;
- struct device *dev = mdev->dev;
- mxr_resource_clear_clocks(res);
- res->mixer = clk_get(dev, "mixer");
- if (IS_ERR(res->mixer)) {
- mxr_err(mdev, "failed to get clock 'mixer'\n");
- goto fail;
- }
- res->vp = clk_get(dev, "vp");
- if (IS_ERR(res->vp)) {
- mxr_err(mdev, "failed to get clock 'vp'\n");
- goto fail;
- }
- res->sclk_mixer = clk_get(dev, "sclk_mixer");
- if (IS_ERR(res->sclk_mixer)) {
- mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
- goto fail;
- }
- res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
- if (IS_ERR(res->sclk_hdmi)) {
- mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
- goto fail;
- }
- res->sclk_dac = clk_get(dev, "sclk_dac");
- if (IS_ERR(res->sclk_dac)) {
- mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
- goto fail;
- }
- return 0;
- fail:
- mxr_release_clocks(mdev);
- return -ENODEV;
- }
- static int mxr_acquire_resources(struct mxr_device *mdev,
- struct platform_device *pdev)
- {
- int ret;
- ret = mxr_acquire_plat_resources(mdev, pdev);
- if (ret)
- goto fail;
- ret = mxr_acquire_clocks(mdev);
- if (ret)
- goto fail_plat;
- mxr_info(mdev, "resources acquired\n");
- return 0;
- fail_plat:
- mxr_release_plat_resources(mdev);
- fail:
- mxr_err(mdev, "resources acquire failed\n");
- return ret;
- }
- static void mxr_release_resources(struct mxr_device *mdev)
- {
- mxr_release_clocks(mdev);
- mxr_release_plat_resources(mdev);
- memset(&mdev->res, 0, sizeof(mdev->res));
- mxr_resource_clear_clocks(&mdev->res);
- }
- static void mxr_release_layers(struct mxr_device *mdev)
- {
- int i;
- for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
- if (mdev->layer[i])
- mxr_layer_release(mdev->layer[i]);
- }
- static int mxr_acquire_layers(struct mxr_device *mdev,
- struct mxr_platform_data *pdata)
- {
- mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
- mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
- mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
- if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
- mxr_err(mdev, "failed to acquire layers\n");
- goto fail;
- }
- return 0;
- fail:
- mxr_release_layers(mdev);
- return -ENODEV;
- }
- /* ---------- POWER MANAGEMENT ----------- */
- static int mxr_runtime_resume(struct device *dev)
- {
- struct mxr_device *mdev = to_mdev(dev);
- struct mxr_resources *res = &mdev->res;
- int ret;
- mxr_dbg(mdev, "resume - start\n");
- mutex_lock(&mdev->mutex);
- /* turn clocks on */
- ret = clk_prepare_enable(res->mixer);
- if (ret < 0) {
- dev_err(mdev->dev, "clk_prepare_enable(mixer) failed\n");
- goto fail;
- }
- ret = clk_prepare_enable(res->vp);
- if (ret < 0) {
- dev_err(mdev->dev, "clk_prepare_enable(vp) failed\n");
- goto fail_mixer;
- }
- ret = clk_prepare_enable(res->sclk_mixer);
- if (ret < 0) {
- dev_err(mdev->dev, "clk_prepare_enable(sclk_mixer) failed\n");
- goto fail_vp;
- }
- /* apply default configuration */
- mxr_reg_reset(mdev);
- mxr_dbg(mdev, "resume - finished\n");
- mutex_unlock(&mdev->mutex);
- return 0;
- fail_vp:
- clk_disable_unprepare(res->vp);
- fail_mixer:
- clk_disable_unprepare(res->mixer);
- fail:
- mutex_unlock(&mdev->mutex);
- dev_err(mdev->dev, "resume failed\n");
- return ret;
- }
- static int mxr_runtime_suspend(struct device *dev)
- {
- struct mxr_device *mdev = to_mdev(dev);
- struct mxr_resources *res = &mdev->res;
- mxr_dbg(mdev, "suspend - start\n");
- mutex_lock(&mdev->mutex);
- /* turn clocks off */
- clk_disable_unprepare(res->sclk_mixer);
- clk_disable_unprepare(res->vp);
- clk_disable_unprepare(res->mixer);
- mutex_unlock(&mdev->mutex);
- mxr_dbg(mdev, "suspend - finished\n");
- return 0;
- }
- static const struct dev_pm_ops mxr_pm_ops = {
- .runtime_suspend = mxr_runtime_suspend,
- .runtime_resume = mxr_runtime_resume,
- };
- /* --------- DRIVER INITIALIZATION ---------- */
- static int mxr_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct mxr_platform_data *pdata = dev->platform_data;
- struct mxr_device *mdev;
- int ret;
- /* mdev does not exist yet so no mxr_dbg is used */
- dev_info(dev, "probe start\n");
- mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
- if (!mdev) {
- dev_err(dev, "not enough memory.\n");
- ret = -ENOMEM;
- goto fail;
- }
- /* setup pointer to master device */
- mdev->dev = dev;
- mutex_init(&mdev->mutex);
- spin_lock_init(&mdev->reg_slock);
- init_waitqueue_head(&mdev->event_queue);
- /* acquire resources: regs, irqs, clocks, regulators */
- ret = mxr_acquire_resources(mdev, pdev);
- if (ret)
- goto fail_mem;
- /* configure resources for video output */
- ret = mxr_acquire_video(mdev, mxr_output_conf,
- ARRAY_SIZE(mxr_output_conf));
- if (ret)
- goto fail_resources;
- /* configure layers */
- ret = mxr_acquire_layers(mdev, pdata);
- if (ret)
- goto fail_video;
- pm_runtime_enable(dev);
- mxr_info(mdev, "probe successful\n");
- return 0;
- fail_video:
- mxr_release_video(mdev);
- fail_resources:
- mxr_release_resources(mdev);
- fail_mem:
- kfree(mdev);
- fail:
- dev_info(dev, "probe failed\n");
- return ret;
- }
- static int mxr_remove(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct mxr_device *mdev = to_mdev(dev);
- pm_runtime_disable(dev);
- mxr_release_layers(mdev);
- mxr_release_video(mdev);
- mxr_release_resources(mdev);
- kfree(mdev);
- dev_info(dev, "remove successful\n");
- return 0;
- }
- static struct platform_driver mxr_driver __refdata = {
- .probe = mxr_probe,
- .remove = mxr_remove,
- .driver = {
- .name = MXR_DRIVER_NAME,
- .pm = &mxr_pm_ops,
- }
- };
- static int __init mxr_init(void)
- {
- int i, ret;
- static const char banner[] __initconst =
- "Samsung TV Mixer driver, "
- "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
- pr_info("%s\n", banner);
- /* Loading auxiliary modules */
- for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
- request_module(mxr_output_conf[i].module_name);
- ret = platform_driver_register(&mxr_driver);
- if (ret != 0) {
- pr_err("s5p-tv: registration of MIXER driver failed\n");
- return -ENXIO;
- }
- return 0;
- }
- module_init(mxr_init);
- static void __exit mxr_exit(void)
- {
- platform_driver_unregister(&mxr_driver);
- }
- module_exit(mxr_exit);
|