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_fb_helper.h> 20#include <drm/drm_crtc_helper.h> 21#include <drm/drm_panel.h> 22#include <linux/videodev2.h> 23#include <video/of_display_timing.h> 24 25#include "imx-drm.h" 26 27#define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector) 28#define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder) 29 30struct imx_parallel_display { 31 struct drm_connector connector; 32 struct drm_encoder encoder; 33 struct device *dev; 34 void *edid; 35 int edid_len; 36 u32 bus_format; 37 int mode_valid; 38 struct drm_display_mode mode; 39 struct drm_panel *panel; 40}; 41 42static enum drm_connector_status imx_pd_connector_detect( 43 struct drm_connector *connector, bool force) 44{ 45 return connector_status_connected; 46} 47 48static int imx_pd_connector_get_modes(struct drm_connector *connector) 49{ 50 struct imx_parallel_display *imxpd = con_to_imxpd(connector); 51 struct device_node *np = imxpd->dev->of_node; 52 int num_modes = 0; 53 54 if (imxpd->panel && imxpd->panel->funcs && 55 imxpd->panel->funcs->get_modes) { 56 num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); 57 if (num_modes > 0) 58 return num_modes; 59 } 60 61 if (imxpd->edid) { 62 drm_mode_connector_update_edid_property(connector, imxpd->edid); 63 num_modes = drm_add_edid_modes(connector, imxpd->edid); 64 } 65 66 if (imxpd->mode_valid) { 67 struct drm_display_mode *mode = drm_mode_create(connector->dev); 68 69 if (!mode) 70 return -EINVAL; 71 drm_mode_copy(mode, &imxpd->mode); 72 mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 73 drm_mode_probed_add(connector, mode); 74 num_modes++; 75 } 76 77 if (np) { 78 struct drm_display_mode *mode = drm_mode_create(connector->dev); 79 80 if (!mode) 81 return -EINVAL; 82 of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE); 83 drm_mode_copy(mode, &imxpd->mode); 84 mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 85 drm_mode_probed_add(connector, mode); 86 num_modes++; 87 } 88 89 return num_modes; 90} 91 92static struct drm_encoder *imx_pd_connector_best_encoder( 93 struct drm_connector *connector) 94{ 95 struct imx_parallel_display *imxpd = con_to_imxpd(connector); 96 97 return &imxpd->encoder; 98} 99 100static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode) 101{ 102 struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); 103 104 if (mode != DRM_MODE_DPMS_ON) 105 drm_panel_disable(imxpd->panel); 106 else 107 drm_panel_enable(imxpd->panel); 108} 109 110static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder, 111 const struct drm_display_mode *mode, 112 struct drm_display_mode *adjusted_mode) 113{ 114 return true; 115} 116 117static void imx_pd_encoder_prepare(struct drm_encoder *encoder) 118{ 119 struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); 120 121 imx_drm_set_bus_format(encoder, imxpd->bus_format); 122} 123 124static void imx_pd_encoder_commit(struct drm_encoder *encoder) 125{ 126 struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); 127 128 drm_panel_prepare(imxpd->panel); 129 drm_panel_enable(imxpd->panel); 130} 131 132static void imx_pd_encoder_mode_set(struct drm_encoder *encoder, 133 struct drm_display_mode *orig_mode, 134 struct drm_display_mode *mode) 135{ 136} 137 138static void imx_pd_encoder_disable(struct drm_encoder *encoder) 139{ 140 struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); 141 142 drm_panel_disable(imxpd->panel); 143 drm_panel_unprepare(imxpd->panel); 144} 145 146static struct drm_connector_funcs imx_pd_connector_funcs = { 147 .dpms = drm_helper_connector_dpms, 148 .fill_modes = drm_helper_probe_single_connector_modes, 149 .detect = imx_pd_connector_detect, 150 .destroy = imx_drm_connector_destroy, 151}; 152 153static 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 struct drm_encoder_funcs imx_pd_encoder_funcs = { 159 .destroy = imx_drm_encoder_destroy, 160}; 161 162static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { 163 .dpms = imx_pd_encoder_dpms, 164 .mode_fixup = imx_pd_encoder_mode_fixup, 165 .prepare = imx_pd_encoder_prepare, 166 .commit = imx_pd_encoder_commit, 167 .mode_set = imx_pd_encoder_mode_set, 168 .disable = imx_pd_encoder_disable, 169}; 170 171static int imx_pd_register(struct drm_device *drm, 172 struct imx_parallel_display *imxpd) 173{ 174 int ret; 175 176 ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, 177 imxpd->dev->of_node); 178 if (ret) 179 return ret; 180 181 /* set the connector's dpms to OFF so that 182 * drm_helper_connector_dpms() won't return 183 * immediately since the current state is ON 184 * at this point. 185 */ 186 imxpd->connector.dpms = DRM_MODE_DPMS_OFF; 187 188 drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); 189 drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, 190 DRM_MODE_ENCODER_NONE); 191 192 drm_connector_helper_add(&imxpd->connector, 193 &imx_pd_connector_helper_funcs); 194 drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs, 195 DRM_MODE_CONNECTOR_VGA); 196 197 if (imxpd->panel) 198 drm_panel_attach(imxpd->panel, &imxpd->connector); 199 200 drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); 201 202 imxpd->connector.encoder = &imxpd->encoder; 203 204 return 0; 205} 206 207static int imx_pd_bind(struct device *dev, struct device *master, void *data) 208{ 209 struct drm_device *drm = data; 210 struct device_node *np = dev->of_node; 211 struct device_node *panel_node; 212 const u8 *edidp; 213 struct imx_parallel_display *imxpd; 214 int ret; 215 const char *fmt; 216 217 imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); 218 if (!imxpd) 219 return -ENOMEM; 220 221 edidp = of_get_property(np, "edid", &imxpd->edid_len); 222 if (edidp) 223 imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL); 224 225 ret = of_property_read_string(np, "interface-pix-fmt", &fmt); 226 if (!ret) { 227 if (!strcmp(fmt, "rgb24")) 228 imxpd->bus_format = MEDIA_BUS_FMT_RGB888_1X24; 229 else if (!strcmp(fmt, "rgb565")) 230 imxpd->bus_format = MEDIA_BUS_FMT_RGB565_1X16; 231 else if (!strcmp(fmt, "bgr666")) 232 imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X18; 233 else if (!strcmp(fmt, "lvds666")) 234 imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; 235 } 236 237 panel_node = of_parse_phandle(np, "fsl,panel", 0); 238 if (panel_node) { 239 imxpd->panel = of_drm_find_panel(panel_node); 240 if (!imxpd->panel) 241 return -EPROBE_DEFER; 242 } 243 244 imxpd->dev = dev; 245 246 ret = imx_pd_register(drm, imxpd); 247 if (ret) 248 return ret; 249 250 dev_set_drvdata(dev, imxpd); 251 252 return 0; 253} 254 255static void imx_pd_unbind(struct device *dev, struct device *master, 256 void *data) 257{ 258 struct imx_parallel_display *imxpd = dev_get_drvdata(dev); 259 260 imxpd->encoder.funcs->destroy(&imxpd->encoder); 261 imxpd->connector.funcs->destroy(&imxpd->connector); 262 263 kfree(imxpd->edid); 264} 265 266static const struct component_ops imx_pd_ops = { 267 .bind = imx_pd_bind, 268 .unbind = imx_pd_unbind, 269}; 270 271static int imx_pd_probe(struct platform_device *pdev) 272{ 273 return component_add(&pdev->dev, &imx_pd_ops); 274} 275 276static int imx_pd_remove(struct platform_device *pdev) 277{ 278 component_del(&pdev->dev, &imx_pd_ops); 279 280 return 0; 281} 282 283static const struct of_device_id imx_pd_dt_ids[] = { 284 { .compatible = "fsl,imx-parallel-display", }, 285 { /* sentinel */ } 286}; 287MODULE_DEVICE_TABLE(of, imx_pd_dt_ids); 288 289static struct platform_driver imx_pd_driver = { 290 .probe = imx_pd_probe, 291 .remove = imx_pd_remove, 292 .driver = { 293 .of_match_table = imx_pd_dt_ids, 294 .name = "imx-parallel-display", 295 }, 296}; 297 298module_platform_driver(imx_pd_driver); 299 300MODULE_DESCRIPTION("i.MX parallel display driver"); 301MODULE_AUTHOR("Sascha Hauer, Pengutronix"); 302MODULE_LICENSE("GPL"); 303MODULE_ALIAS("platform:imx-parallel-display"); 304