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