linux/drivers/gpu/drm/imx/parallel-display.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * i.MX drm driver - parallel display implementation
   4 *
   5 * Copyright (C) 2012 Sascha Hauer, Pengutronix
   6 */
   7
   8#include <linux/component.h>
   9#include <linux/module.h>
  10#include <drm/drmP.h>
  11#include <drm/drm_atomic_helper.h>
  12#include <drm/drm_fb_helper.h>
  13#include <drm/drm_of.h>
  14#include <drm/drm_panel.h>
  15#include <drm/drm_probe_helper.h>
  16#include <linux/videodev2.h>
  17#include <video/of_display_timing.h>
  18
  19#include "imx-drm.h"
  20
  21struct imx_parallel_display {
  22        struct drm_connector connector;
  23        struct drm_encoder encoder;
  24        struct device *dev;
  25        void *edid;
  26        int edid_len;
  27        u32 bus_format;
  28        u32 bus_flags;
  29        struct drm_display_mode mode;
  30        struct drm_panel *panel;
  31        struct drm_bridge *bridge;
  32};
  33
  34static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
  35{
  36        return container_of(c, struct imx_parallel_display, connector);
  37}
  38
  39static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e)
  40{
  41        return container_of(e, struct imx_parallel_display, encoder);
  42}
  43
  44static int imx_pd_connector_get_modes(struct drm_connector *connector)
  45{
  46        struct imx_parallel_display *imxpd = con_to_imxpd(connector);
  47        struct device_node *np = imxpd->dev->of_node;
  48        int num_modes = 0;
  49
  50        if (imxpd->panel && imxpd->panel->funcs &&
  51            imxpd->panel->funcs->get_modes) {
  52                num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
  53                if (num_modes > 0)
  54                        return num_modes;
  55        }
  56
  57        if (imxpd->edid) {
  58                drm_connector_update_edid_property(connector, imxpd->edid);
  59                num_modes = drm_add_edid_modes(connector, imxpd->edid);
  60        }
  61
  62        if (np) {
  63                struct drm_display_mode *mode = drm_mode_create(connector->dev);
  64                int ret;
  65
  66                if (!mode)
  67                        return -EINVAL;
  68
  69                ret = of_get_drm_display_mode(np, &imxpd->mode,
  70                                              &imxpd->bus_flags,
  71                                              OF_USE_NATIVE_MODE);
  72                if (ret)
  73                        return ret;
  74
  75                drm_mode_copy(mode, &imxpd->mode);
  76                mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
  77                drm_mode_probed_add(connector, mode);
  78                num_modes++;
  79        }
  80
  81        return num_modes;
  82}
  83
  84static struct drm_encoder *imx_pd_connector_best_encoder(
  85                struct drm_connector *connector)
  86{
  87        struct imx_parallel_display *imxpd = con_to_imxpd(connector);
  88
  89        return &imxpd->encoder;
  90}
  91
  92static void imx_pd_encoder_enable(struct drm_encoder *encoder)
  93{
  94        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
  95
  96        drm_panel_prepare(imxpd->panel);
  97        drm_panel_enable(imxpd->panel);
  98}
  99
 100static void imx_pd_encoder_disable(struct drm_encoder *encoder)
 101{
 102        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 103
 104        drm_panel_disable(imxpd->panel);
 105        drm_panel_unprepare(imxpd->panel);
 106}
 107
 108static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
 109                                       struct drm_crtc_state *crtc_state,
 110                                       struct drm_connector_state *conn_state)
 111{
 112        struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
 113        struct drm_display_info *di = &conn_state->connector->display_info;
 114        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 115
 116        if (!imxpd->bus_format && di->num_bus_formats) {
 117                imx_crtc_state->bus_flags = di->bus_flags;
 118                imx_crtc_state->bus_format = di->bus_formats[0];
 119        } else {
 120                imx_crtc_state->bus_flags = imxpd->bus_flags;
 121                imx_crtc_state->bus_format = imxpd->bus_format;
 122        }
 123        imx_crtc_state->di_hsync_pin = 2;
 124        imx_crtc_state->di_vsync_pin = 3;
 125
 126        return 0;
 127}
 128
 129static const struct drm_connector_funcs imx_pd_connector_funcs = {
 130        .fill_modes = drm_helper_probe_single_connector_modes,
 131        .destroy = imx_drm_connector_destroy,
 132        .reset = drm_atomic_helper_connector_reset,
 133        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 134        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 135};
 136
 137static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
 138        .get_modes = imx_pd_connector_get_modes,
 139        .best_encoder = imx_pd_connector_best_encoder,
 140};
 141
 142static const struct drm_encoder_funcs imx_pd_encoder_funcs = {
 143        .destroy = imx_drm_encoder_destroy,
 144};
 145
 146static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
 147        .enable = imx_pd_encoder_enable,
 148        .disable = imx_pd_encoder_disable,
 149        .atomic_check = imx_pd_encoder_atomic_check,
 150};
 151
 152static int imx_pd_register(struct drm_device *drm,
 153        struct imx_parallel_display *imxpd)
 154{
 155        struct drm_encoder *encoder = &imxpd->encoder;
 156        int ret;
 157
 158        ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
 159        if (ret)
 160                return ret;
 161
 162        /* set the connector's dpms to OFF so that
 163         * drm_helper_connector_dpms() won't return
 164         * immediately since the current state is ON
 165         * at this point.
 166         */
 167        imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
 168
 169        drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs);
 170        drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs,
 171                         DRM_MODE_ENCODER_NONE, NULL);
 172
 173        if (!imxpd->bridge) {
 174                drm_connector_helper_add(&imxpd->connector,
 175                                &imx_pd_connector_helper_funcs);
 176                drm_connector_init(drm, &imxpd->connector,
 177                                   &imx_pd_connector_funcs,
 178                                   DRM_MODE_CONNECTOR_DPI);
 179        }
 180
 181        if (imxpd->panel)
 182                drm_panel_attach(imxpd->panel, &imxpd->connector);
 183
 184        if (imxpd->bridge) {
 185                ret = drm_bridge_attach(encoder, imxpd->bridge, NULL);
 186                if (ret < 0) {
 187                        dev_err(imxpd->dev, "failed to attach bridge: %d\n",
 188                                ret);
 189                        return ret;
 190                }
 191        } else {
 192                drm_connector_attach_encoder(&imxpd->connector, encoder);
 193        }
 194
 195        return 0;
 196}
 197
 198static int imx_pd_bind(struct device *dev, struct device *master, void *data)
 199{
 200        struct drm_device *drm = data;
 201        struct device_node *np = dev->of_node;
 202        const u8 *edidp;
 203        struct imx_parallel_display *imxpd;
 204        int ret;
 205        u32 bus_format = 0;
 206        const char *fmt;
 207
 208        imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
 209        if (!imxpd)
 210                return -ENOMEM;
 211
 212        edidp = of_get_property(np, "edid", &imxpd->edid_len);
 213        if (edidp)
 214                imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
 215
 216        ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
 217        if (!ret) {
 218                if (!strcmp(fmt, "rgb24"))
 219                        bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 220                else if (!strcmp(fmt, "rgb565"))
 221                        bus_format = MEDIA_BUS_FMT_RGB565_1X16;
 222                else if (!strcmp(fmt, "bgr666"))
 223                        bus_format = MEDIA_BUS_FMT_RGB666_1X18;
 224                else if (!strcmp(fmt, "lvds666"))
 225                        bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
 226        }
 227        imxpd->bus_format = bus_format;
 228
 229        /* port@1 is the output port */
 230        ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge);
 231        if (ret && ret != -ENODEV)
 232                return ret;
 233
 234        imxpd->dev = dev;
 235
 236        ret = imx_pd_register(drm, imxpd);
 237        if (ret)
 238                return ret;
 239
 240        dev_set_drvdata(dev, imxpd);
 241
 242        return 0;
 243}
 244
 245static void imx_pd_unbind(struct device *dev, struct device *master,
 246        void *data)
 247{
 248        struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
 249
 250        if (imxpd->panel)
 251                drm_panel_detach(imxpd->panel);
 252
 253        kfree(imxpd->edid);
 254}
 255
 256static const struct component_ops imx_pd_ops = {
 257        .bind   = imx_pd_bind,
 258        .unbind = imx_pd_unbind,
 259};
 260
 261static int imx_pd_probe(struct platform_device *pdev)
 262{
 263        return component_add(&pdev->dev, &imx_pd_ops);
 264}
 265
 266static int imx_pd_remove(struct platform_device *pdev)
 267{
 268        component_del(&pdev->dev, &imx_pd_ops);
 269
 270        return 0;
 271}
 272
 273static const struct of_device_id imx_pd_dt_ids[] = {
 274        { .compatible = "fsl,imx-parallel-display", },
 275        { /* sentinel */ }
 276};
 277MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);
 278
 279static struct platform_driver imx_pd_driver = {
 280        .probe          = imx_pd_probe,
 281        .remove         = imx_pd_remove,
 282        .driver         = {
 283                .of_match_table = imx_pd_dt_ids,
 284                .name   = "imx-parallel-display",
 285        },
 286};
 287
 288module_platform_driver(imx_pd_driver);
 289
 290MODULE_DESCRIPTION("i.MX parallel display driver");
 291MODULE_AUTHOR("Sascha Hauer, Pengutronix");
 292MODULE_LICENSE("GPL");
 293MODULE_ALIAS("platform:imx-parallel-display");
 294