linux/drivers/video/omap2/displays-new/connector-hdmi.c
<<
>>
Prefs
   1/*
   2 * HDMI Connector driver
   3 *
   4 * Copyright (C) 2013 Texas Instruments
   5 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 */
  11
  12#include <linux/slab.h>
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15
  16#include <drm/drm_edid.h>
  17
  18#include <video/omapdss.h>
  19#include <video/omap-panel-data.h>
  20
  21static const struct omap_video_timings hdmic_default_timings = {
  22        .x_res          = 640,
  23        .y_res          = 480,
  24        .pixel_clock    = 25175,
  25        .hsw            = 96,
  26        .hfp            = 16,
  27        .hbp            = 48,
  28        .vsw            = 2,
  29        .vfp            = 11,
  30        .vbp            = 31,
  31
  32        .vsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  33        .hsync_level    = OMAPDSS_SIG_ACTIVE_LOW,
  34
  35        .interlace      = false,
  36};
  37
  38struct panel_drv_data {
  39        struct omap_dss_device dssdev;
  40        struct omap_dss_device *in;
  41
  42        struct device *dev;
  43
  44        struct omap_video_timings timings;
  45};
  46
  47#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
  48
  49static int hdmic_connect(struct omap_dss_device *dssdev)
  50{
  51        struct panel_drv_data *ddata = to_panel_data(dssdev);
  52        struct omap_dss_device *in = ddata->in;
  53        int r;
  54
  55        dev_dbg(ddata->dev, "connect\n");
  56
  57        if (omapdss_device_is_connected(dssdev))
  58                return 0;
  59
  60        r = in->ops.hdmi->connect(in, dssdev);
  61        if (r)
  62                return r;
  63
  64        return 0;
  65}
  66
  67static void hdmic_disconnect(struct omap_dss_device *dssdev)
  68{
  69        struct panel_drv_data *ddata = to_panel_data(dssdev);
  70        struct omap_dss_device *in = ddata->in;
  71
  72        dev_dbg(ddata->dev, "disconnect\n");
  73
  74        if (!omapdss_device_is_connected(dssdev))
  75                return;
  76
  77        in->ops.hdmi->disconnect(in, dssdev);
  78}
  79
  80static int hdmic_enable(struct omap_dss_device *dssdev)
  81{
  82        struct panel_drv_data *ddata = to_panel_data(dssdev);
  83        struct omap_dss_device *in = ddata->in;
  84        int r;
  85
  86        dev_dbg(ddata->dev, "enable\n");
  87
  88        if (!omapdss_device_is_connected(dssdev))
  89                return -ENODEV;
  90
  91        if (omapdss_device_is_enabled(dssdev))
  92                return 0;
  93
  94        in->ops.hdmi->set_timings(in, &ddata->timings);
  95
  96        r = in->ops.hdmi->enable(in);
  97        if (r)
  98                return r;
  99
 100        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 101
 102        return r;
 103}
 104
 105static void hdmic_disable(struct omap_dss_device *dssdev)
 106{
 107        struct panel_drv_data *ddata = to_panel_data(dssdev);
 108        struct omap_dss_device *in = ddata->in;
 109
 110        dev_dbg(ddata->dev, "disable\n");
 111
 112        if (!omapdss_device_is_enabled(dssdev))
 113                return;
 114
 115        in->ops.hdmi->disable(in);
 116
 117        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 118}
 119
 120static void hdmic_set_timings(struct omap_dss_device *dssdev,
 121                struct omap_video_timings *timings)
 122{
 123        struct panel_drv_data *ddata = to_panel_data(dssdev);
 124        struct omap_dss_device *in = ddata->in;
 125
 126        ddata->timings = *timings;
 127        dssdev->panel.timings = *timings;
 128
 129        in->ops.hdmi->set_timings(in, timings);
 130}
 131
 132static void hdmic_get_timings(struct omap_dss_device *dssdev,
 133                struct omap_video_timings *timings)
 134{
 135        struct panel_drv_data *ddata = to_panel_data(dssdev);
 136
 137        *timings = ddata->timings;
 138}
 139
 140static int hdmic_check_timings(struct omap_dss_device *dssdev,
 141                struct omap_video_timings *timings)
 142{
 143        struct panel_drv_data *ddata = to_panel_data(dssdev);
 144        struct omap_dss_device *in = ddata->in;
 145
 146        return in->ops.hdmi->check_timings(in, timings);
 147}
 148
 149static int hdmic_read_edid(struct omap_dss_device *dssdev,
 150                u8 *edid, int len)
 151{
 152        struct panel_drv_data *ddata = to_panel_data(dssdev);
 153        struct omap_dss_device *in = ddata->in;
 154
 155        return in->ops.hdmi->read_edid(in, edid, len);
 156}
 157
 158static bool hdmic_detect(struct omap_dss_device *dssdev)
 159{
 160        struct panel_drv_data *ddata = to_panel_data(dssdev);
 161        struct omap_dss_device *in = ddata->in;
 162
 163        return in->ops.hdmi->detect(in);
 164}
 165
 166static int hdmic_audio_enable(struct omap_dss_device *dssdev)
 167{
 168        struct panel_drv_data *ddata = to_panel_data(dssdev);
 169        struct omap_dss_device *in = ddata->in;
 170        int r;
 171
 172        /* enable audio only if the display is active */
 173        if (!omapdss_device_is_enabled(dssdev))
 174                return -EPERM;
 175
 176        r = in->ops.hdmi->audio_enable(in);
 177        if (r)
 178                return r;
 179
 180        dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
 181
 182        return 0;
 183}
 184
 185static void hdmic_audio_disable(struct omap_dss_device *dssdev)
 186{
 187        struct panel_drv_data *ddata = to_panel_data(dssdev);
 188        struct omap_dss_device *in = ddata->in;
 189
 190        in->ops.hdmi->audio_disable(in);
 191
 192        dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
 193}
 194
 195static int hdmic_audio_start(struct omap_dss_device *dssdev)
 196{
 197        struct panel_drv_data *ddata = to_panel_data(dssdev);
 198        struct omap_dss_device *in = ddata->in;
 199        int r;
 200
 201        /*
 202         * No need to check the panel state. It was checked when trasitioning
 203         * to AUDIO_ENABLED.
 204         */
 205        if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED)
 206                return -EPERM;
 207
 208        r = in->ops.hdmi->audio_start(in);
 209        if (r)
 210                return r;
 211
 212        dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
 213
 214        return 0;
 215}
 216
 217static void hdmic_audio_stop(struct omap_dss_device *dssdev)
 218{
 219        struct panel_drv_data *ddata = to_panel_data(dssdev);
 220        struct omap_dss_device *in = ddata->in;
 221
 222        in->ops.hdmi->audio_stop(in);
 223
 224        dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
 225}
 226
 227static bool hdmic_audio_supported(struct omap_dss_device *dssdev)
 228{
 229        struct panel_drv_data *ddata = to_panel_data(dssdev);
 230        struct omap_dss_device *in = ddata->in;
 231
 232        if (!omapdss_device_is_enabled(dssdev))
 233                return false;
 234
 235        return in->ops.hdmi->audio_supported(in);
 236}
 237
 238static int hdmic_audio_config(struct omap_dss_device *dssdev,
 239                struct omap_dss_audio *audio)
 240{
 241        struct panel_drv_data *ddata = to_panel_data(dssdev);
 242        struct omap_dss_device *in = ddata->in;
 243        int r;
 244
 245        /* config audio only if the display is active */
 246        if (!omapdss_device_is_enabled(dssdev))
 247                return -EPERM;
 248
 249        r = in->ops.hdmi->audio_config(in, audio);
 250        if (r)
 251                return r;
 252
 253        dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
 254
 255        return 0;
 256}
 257
 258static struct omap_dss_driver hdmic_driver = {
 259        .connect                = hdmic_connect,
 260        .disconnect             = hdmic_disconnect,
 261
 262        .enable                 = hdmic_enable,
 263        .disable                = hdmic_disable,
 264
 265        .set_timings            = hdmic_set_timings,
 266        .get_timings            = hdmic_get_timings,
 267        .check_timings          = hdmic_check_timings,
 268
 269        .get_resolution         = omapdss_default_get_resolution,
 270
 271        .read_edid              = hdmic_read_edid,
 272        .detect                 = hdmic_detect,
 273
 274        .audio_enable           = hdmic_audio_enable,
 275        .audio_disable          = hdmic_audio_disable,
 276        .audio_start            = hdmic_audio_start,
 277        .audio_stop             = hdmic_audio_stop,
 278        .audio_supported        = hdmic_audio_supported,
 279        .audio_config           = hdmic_audio_config,
 280};
 281
 282static int hdmic_probe_pdata(struct platform_device *pdev)
 283{
 284        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 285        struct connector_hdmi_platform_data *pdata;
 286        struct omap_dss_device *in, *dssdev;
 287
 288        pdata = dev_get_platdata(&pdev->dev);
 289
 290        in = omap_dss_find_output(pdata->source);
 291        if (in == NULL) {
 292                dev_err(&pdev->dev, "Failed to find video source\n");
 293                return -ENODEV;
 294        }
 295
 296        ddata->in = in;
 297
 298        dssdev = &ddata->dssdev;
 299        dssdev->name = pdata->name;
 300
 301        return 0;
 302}
 303
 304static int hdmic_probe(struct platform_device *pdev)
 305{
 306        struct panel_drv_data *ddata;
 307        struct omap_dss_device *dssdev;
 308        int r;
 309
 310        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
 311        if (!ddata)
 312                return -ENOMEM;
 313
 314        platform_set_drvdata(pdev, ddata);
 315        ddata->dev = &pdev->dev;
 316
 317        if (dev_get_platdata(&pdev->dev)) {
 318                r = hdmic_probe_pdata(pdev);
 319                if (r)
 320                        return r;
 321        } else {
 322                return -ENODEV;
 323        }
 324
 325        ddata->timings = hdmic_default_timings;
 326
 327        dssdev = &ddata->dssdev;
 328        dssdev->driver = &hdmic_driver;
 329        dssdev->dev = &pdev->dev;
 330        dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
 331        dssdev->owner = THIS_MODULE;
 332        dssdev->panel.timings = hdmic_default_timings;
 333
 334        r = omapdss_register_display(dssdev);
 335        if (r) {
 336                dev_err(&pdev->dev, "Failed to register panel\n");
 337                goto err_reg;
 338        }
 339
 340        return 0;
 341err_reg:
 342        omap_dss_put_device(ddata->in);
 343        return r;
 344}
 345
 346static int __exit hdmic_remove(struct platform_device *pdev)
 347{
 348        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
 349        struct omap_dss_device *dssdev = &ddata->dssdev;
 350        struct omap_dss_device *in = ddata->in;
 351
 352        omapdss_unregister_display(&ddata->dssdev);
 353
 354        hdmic_disable(dssdev);
 355        hdmic_disconnect(dssdev);
 356
 357        omap_dss_put_device(in);
 358
 359        return 0;
 360}
 361
 362static struct platform_driver hdmi_connector_driver = {
 363        .probe  = hdmic_probe,
 364        .remove = __exit_p(hdmic_remove),
 365        .driver = {
 366                .name   = "connector-hdmi",
 367                .owner  = THIS_MODULE,
 368        },
 369};
 370
 371module_platform_driver(hdmi_connector_driver);
 372
 373MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 374MODULE_DESCRIPTION("HDMI Connector driver");
 375MODULE_LICENSE("GPL");
 376