linux/drivers/gpu/drm/imx/parallel-display.c
<<
>>
Prefs
   1/*
   2 * i.MX drm driver - parallel display implementation
   3 *
   4 * Copyright (C) 2012 Sascha Hauer, Pengutronix
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version 2
   9 * of the License, or (at your option) any later version.
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/component.h>
  17#include <linux/module.h>
  18#include <drm/drmP.h>
  19#include <drm/drm_atomic_helper.h>
  20#include <drm/drm_fb_helper.h>
  21#include <drm/drm_crtc_helper.h>
  22#include <drm/drm_panel.h>
  23#include <linux/videodev2.h>
  24#include <video/of_display_timing.h>
  25#include <linux/of_graph.h>
  26
  27#include "imx-drm.h"
  28
  29struct imx_parallel_display {
  30        struct drm_connector connector;
  31        struct drm_encoder encoder;
  32        struct device *dev;
  33        void *edid;
  34        int edid_len;
  35        u32 bus_format;
  36        u32 bus_flags;
  37        struct drm_display_mode mode;
  38        struct drm_panel *panel;
  39        struct drm_bridge *bridge;
  40};
  41
  42static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
  43{
  44        return container_of(c, struct imx_parallel_display, connector);
  45}
  46
  47static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e)
  48{
  49        return container_of(e, struct imx_parallel_display, encoder);
  50}
  51
  52static enum drm_connector_status imx_pd_connector_detect(
  53                struct drm_connector *connector, bool force)
  54{
  55        return connector_status_connected;
  56}
  57
  58static int imx_pd_connector_get_modes(struct drm_connector *connector)
  59{
  60        struct imx_parallel_display *imxpd = con_to_imxpd(connector);
  61        struct device_node *np = imxpd->dev->of_node;
  62        int num_modes = 0;
  63
  64        if (imxpd->panel && imxpd->panel->funcs &&
  65            imxpd->panel->funcs->get_modes) {
  66                num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
  67                if (num_modes > 0)
  68                        return num_modes;
  69        }
  70
  71        if (imxpd->edid) {
  72                drm_mode_connector_update_edid_property(connector, imxpd->edid);
  73                num_modes = drm_add_edid_modes(connector, imxpd->edid);
  74        }
  75
  76        if (np) {
  77                struct drm_display_mode *mode = drm_mode_create(connector->dev);
  78                int ret;
  79
  80                if (!mode)
  81                        return -EINVAL;
  82
  83                ret = of_get_drm_display_mode(np, &imxpd->mode,
  84                                              &imxpd->bus_flags,
  85                                              OF_USE_NATIVE_MODE);
  86                if (ret)
  87                        return ret;
  88
  89                drm_mode_copy(mode, &imxpd->mode);
  90                mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
  91                drm_mode_probed_add(connector, mode);
  92                num_modes++;
  93        }
  94
  95        return num_modes;
  96}
  97
  98static struct drm_encoder *imx_pd_connector_best_encoder(
  99                struct drm_connector *connector)
 100{
 101        struct imx_parallel_display *imxpd = con_to_imxpd(connector);
 102
 103        return &imxpd->encoder;
 104}
 105
 106static void imx_pd_encoder_enable(struct drm_encoder *encoder)
 107{
 108        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 109
 110        drm_panel_prepare(imxpd->panel);
 111        drm_panel_enable(imxpd->panel);
 112}
 113
 114static void imx_pd_encoder_disable(struct drm_encoder *encoder)
 115{
 116        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 117
 118        drm_panel_disable(imxpd->panel);
 119        drm_panel_unprepare(imxpd->panel);
 120}
 121
 122static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
 123                                       struct drm_crtc_state *crtc_state,
 124                                       struct drm_connector_state *conn_state)
 125{
 126        struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
 127        struct drm_display_info *di = &conn_state->connector->display_info;
 128        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 129
 130        if (!imxpd->bus_format && di->num_bus_formats) {
 131                imx_crtc_state->bus_flags = di->bus_flags;
 132                imx_crtc_state->bus_format = di->bus_formats[0];
 133        } else {
 134                imx_crtc_state->bus_flags = imxpd->bus_flags;
 135                imx_crtc_state->bus_format = imxpd->bus_format;
 136        }
 137        imx_crtc_state->di_hsync_pin = 2;
 138        imx_crtc_state->di_vsync_pin = 3;
 139
 140        return 0;
 141}
 142
 143static const struct drm_connector_funcs imx_pd_connector_funcs = {
 144        .dpms = drm_atomic_helper_connector_dpms,
 145        .fill_modes = drm_helper_probe_single_connector_modes,
 146        .detect = imx_pd_connector_detect,
 147        .destroy = imx_drm_connector_destroy,
 148        .reset = drm_atomic_helper_connector_reset,
 149        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 150        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 151};
 152
 153static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
 154        .get_modes = imx_pd_connector_get_modes,
 155        .best_encoder = imx_pd_connector_best_encoder,
 156};
 157
 158static const struct drm_encoder_funcs imx_pd_encoder_funcs = {
 159        .destroy = imx_drm_encoder_destroy,
 160};
 161
 162static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
 163        .enable = imx_pd_encoder_enable,
 164        .disable = imx_pd_encoder_disable,
 165        .atomic_check = imx_pd_encoder_atomic_check,
 166};
 167
 168static int imx_pd_register(struct drm_device *drm,
 169        struct imx_parallel_display *imxpd)
 170{
 171        struct drm_encoder *encoder = &imxpd->encoder;
 172        int ret;
 173
 174        ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
 175        if (ret)
 176                return ret;
 177
 178        /* set the connector's dpms to OFF so that
 179         * drm_helper_connector_dpms() won't return
 180         * immediately since the current state is ON
 181         * at this point.
 182         */
 183        imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
 184
 185        drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs);
 186        drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs,
 187                         DRM_MODE_ENCODER_NONE, NULL);
 188
 189        if (!imxpd->bridge) {
 190                drm_connector_helper_add(&imxpd->connector,
 191                                &imx_pd_connector_helper_funcs);
 192                drm_connector_init(drm, &imxpd->connector,
 193                                   &imx_pd_connector_funcs,
 194                                   DRM_MODE_CONNECTOR_VGA);
 195        }
 196
 197        if (imxpd->panel)
 198                drm_panel_attach(imxpd->panel, &imxpd->connector);
 199
 200        if (imxpd->bridge) {
 201                imxpd->bridge->encoder = encoder;
 202                encoder->bridge = imxpd->bridge;
 203                ret = drm_bridge_attach(drm, imxpd->bridge);
 204                if (ret < 0) {
 205                        dev_err(imxpd->dev, "failed to attach bridge: %d\n",
 206                                ret);
 207                        return ret;
 208                }
 209        } else {
 210                drm_mode_connector_attach_encoder(&imxpd->connector, encoder);
 211        }
 212
 213        return 0;
 214}
 215
 216static int imx_pd_bind(struct device *dev, struct device *master, void *data)
 217{
 218        struct drm_device *drm = data;
 219        struct device_node *np = dev->of_node;
 220        struct device_node *ep;
 221        const u8 *edidp;
 222        struct imx_parallel_display *imxpd;
 223        int ret;
 224        u32 bus_format = 0;
 225        const char *fmt;
 226
 227        imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
 228        if (!imxpd)
 229                return -ENOMEM;
 230
 231        edidp = of_get_property(np, "edid", &imxpd->edid_len);
 232        if (edidp)
 233                imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
 234
 235        ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
 236        if (!ret) {
 237                if (!strcmp(fmt, "rgb24"))
 238                        bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 239                else if (!strcmp(fmt, "rgb565"))
 240                        bus_format = MEDIA_BUS_FMT_RGB565_1X16;
 241                else if (!strcmp(fmt, "bgr666"))
 242                        bus_format = MEDIA_BUS_FMT_RGB666_1X18;
 243                else if (!strcmp(fmt, "lvds666"))
 244                        bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
 245        }
 246        imxpd->bus_format = bus_format;
 247
 248        /* port@1 is the output port */
 249        ep = of_graph_get_endpoint_by_regs(np, 1, -1);
 250        if (ep) {
 251                struct device_node *remote;
 252
 253                remote = of_graph_get_remote_port_parent(ep);
 254                if (!remote) {
 255                        dev_warn(dev, "endpoint %s not connected\n",
 256                                 ep->full_name);
 257                        of_node_put(ep);
 258                        return -ENODEV;
 259                }
 260                of_node_put(ep);
 261
 262                imxpd->panel = of_drm_find_panel(remote);
 263                if (imxpd->panel) {
 264                        dev_dbg(dev, "found panel %s\n", remote->full_name);
 265                } else {
 266                        imxpd->bridge = of_drm_find_bridge(remote);
 267                        if (imxpd->bridge)
 268                                dev_dbg(dev, "found bridge %s\n",
 269                                        remote->full_name);
 270                }
 271                if (!imxpd->panel && !imxpd->bridge) {
 272                        dev_dbg(dev, "waiting for panel or bridge %s\n",
 273                                remote->full_name);
 274                        of_node_put(remote);
 275                        return -EPROBE_DEFER;
 276                }
 277                of_node_put(remote);
 278        }
 279
 280        imxpd->dev = dev;
 281
 282        ret = imx_pd_register(drm, imxpd);
 283        if (ret)
 284                return ret;
 285
 286        dev_set_drvdata(dev, imxpd);
 287
 288        return 0;
 289}
 290
 291static void imx_pd_unbind(struct device *dev, struct device *master,
 292        void *data)
 293{
 294        struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
 295
 296        if (imxpd->bridge)
 297                drm_bridge_detach(imxpd->bridge);
 298        if (imxpd->panel)
 299                drm_panel_detach(imxpd->panel);
 300
 301        kfree(imxpd->edid);
 302}
 303
 304static const struct component_ops imx_pd_ops = {
 305        .bind   = imx_pd_bind,
 306        .unbind = imx_pd_unbind,
 307};
 308
 309static int imx_pd_probe(struct platform_device *pdev)
 310{
 311        return component_add(&pdev->dev, &imx_pd_ops);
 312}
 313
 314static int imx_pd_remove(struct platform_device *pdev)
 315{
 316        component_del(&pdev->dev, &imx_pd_ops);
 317
 318        return 0;
 319}
 320
 321static const struct of_device_id imx_pd_dt_ids[] = {
 322        { .compatible = "fsl,imx-parallel-display", },
 323        { /* sentinel */ }
 324};
 325MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);
 326
 327static struct platform_driver imx_pd_driver = {
 328        .probe          = imx_pd_probe,
 329        .remove         = imx_pd_remove,
 330        .driver         = {
 331                .of_match_table = imx_pd_dt_ids,
 332                .name   = "imx-parallel-display",
 333        },
 334};
 335
 336module_platform_driver(imx_pd_driver);
 337
 338MODULE_DESCRIPTION("i.MX parallel display driver");
 339MODULE_AUTHOR("Sascha Hauer, Pengutronix");
 340MODULE_LICENSE("GPL");
 341MODULE_ALIAS("platform:imx-parallel-display");
 342