linux/drivers/media/platform/s5p-tv/mixer_drv.c
<<
>>
Prefs
   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;
  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
 154static int 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
 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
 214static 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
 221static 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
 237static 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(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(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(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(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(res->sclk_dac)) {
 264                mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
 265                goto fail;
 266        }
 267
 268        return 0;
 269fail:
 270        mxr_release_clocks(mdev);
 271        return -ENODEV;
 272}
 273
 274static int 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
 290fail_plat:
 291        mxr_release_plat_resources(mdev);
 292fail:
 293        mxr_err(mdev, "resources acquire failed\n");
 294        return ret;
 295}
 296
 297static 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
 304static 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
 313static int 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
 327fail:
 328        mxr_release_layers(mdev);
 329        return -ENODEV;
 330}
 331
 332/* ---------- POWER MANAGEMENT ----------- */
 333
 334static 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
 353static 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
 368static 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
 375static int 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                dev_err(dev, "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
 420fail_video:
 421        mxr_release_video(mdev);
 422
 423fail_resources:
 424        mxr_release_resources(mdev);
 425
 426fail_mem:
 427        kfree(mdev);
 428
 429fail:
 430        dev_info(dev, "probe failed\n");
 431        return ret;
 432}
 433
 434static int 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 successful\n");
 448        return 0;
 449}
 450
 451static struct platform_driver mxr_driver __refdata = {
 452        .probe = mxr_probe,
 453        .remove = mxr_remove,
 454        .driver = {
 455                .name = MXR_DRIVER_NAME,
 456                .owner = THIS_MODULE,
 457                .pm = &mxr_pm_ops,
 458        }
 459};
 460
 461static int __init mxr_init(void)
 462{
 463        int i, ret;
 464        static const char banner[] __initconst =
 465                "Samsung TV Mixer driver, "
 466                "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
 467        pr_info("%s\n", 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                pr_err("s5p-tv: registration of MIXER driver failed\n");
 476                return -ENXIO;
 477        }
 478
 479        return 0;
 480}
 481module_init(mxr_init);
 482
 483static void __exit mxr_exit(void)
 484{
 485        platform_driver_unregister(&mxr_driver);
 486}
 487module_exit(mxr_exit);
 488
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.