14b6cb2b6SLucas Stach // SPDX-License-Identifier: GPL-2.0+
24b6cb2b6SLucas Stach /*
34b6cb2b6SLucas Stach  * i.MX drm driver - parallel display implementation
44b6cb2b6SLucas Stach  *
54b6cb2b6SLucas Stach  * Copyright (C) 2012 Sascha Hauer, Pengutronix
64b6cb2b6SLucas Stach  */
74b6cb2b6SLucas Stach 
84b6cb2b6SLucas Stach #include <linux/component.h>
94b6cb2b6SLucas Stach #include <linux/media-bus-format.h>
104b6cb2b6SLucas Stach #include <linux/module.h>
114b6cb2b6SLucas Stach #include <linux/of.h>
124b6cb2b6SLucas Stach #include <linux/platform_device.h>
134b6cb2b6SLucas Stach #include <linux/videodev2.h>
144b6cb2b6SLucas Stach 
154b6cb2b6SLucas Stach #include <video/of_display_timing.h>
164b6cb2b6SLucas Stach 
174b6cb2b6SLucas Stach #include <drm/drm_atomic_helper.h>
184b6cb2b6SLucas Stach #include <drm/drm_bridge.h>
194b6cb2b6SLucas Stach #include <drm/drm_edid.h>
204b6cb2b6SLucas Stach #include <drm/drm_managed.h>
214b6cb2b6SLucas Stach #include <drm/drm_of.h>
224b6cb2b6SLucas Stach #include <drm/drm_panel.h>
234b6cb2b6SLucas Stach #include <drm/drm_probe_helper.h>
244b6cb2b6SLucas Stach #include <drm/drm_simple_kms_helper.h>
254b6cb2b6SLucas Stach 
264b6cb2b6SLucas Stach #include "imx-drm.h"
274b6cb2b6SLucas Stach 
284b6cb2b6SLucas Stach struct imx_parallel_display_encoder {
294b6cb2b6SLucas Stach 	struct drm_connector connector;
304b6cb2b6SLucas Stach 	struct drm_encoder encoder;
314b6cb2b6SLucas Stach 	struct drm_bridge bridge;
324b6cb2b6SLucas Stach 	struct imx_parallel_display *pd;
334b6cb2b6SLucas Stach };
344b6cb2b6SLucas Stach 
354b6cb2b6SLucas Stach struct imx_parallel_display {
364b6cb2b6SLucas Stach 	struct device *dev;
374b6cb2b6SLucas Stach 	void *edid;
384b6cb2b6SLucas Stach 	u32 bus_format;
394b6cb2b6SLucas Stach 	u32 bus_flags;
404b6cb2b6SLucas Stach 	struct drm_display_mode mode;
414b6cb2b6SLucas Stach 	struct drm_panel *panel;
424b6cb2b6SLucas Stach 	struct drm_bridge *next_bridge;
434b6cb2b6SLucas Stach };
444b6cb2b6SLucas Stach 
con_to_imxpd(struct drm_connector * c)454b6cb2b6SLucas Stach static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
464b6cb2b6SLucas Stach {
474b6cb2b6SLucas Stach 	return container_of(c, struct imx_parallel_display_encoder, connector)->pd;
484b6cb2b6SLucas Stach }
494b6cb2b6SLucas Stach 
bridge_to_imxpd(struct drm_bridge * b)504b6cb2b6SLucas Stach static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b)
514b6cb2b6SLucas Stach {
524b6cb2b6SLucas Stach 	return container_of(b, struct imx_parallel_display_encoder, bridge)->pd;
534b6cb2b6SLucas Stach }
544b6cb2b6SLucas Stach 
imx_pd_connector_get_modes(struct drm_connector * connector)554b6cb2b6SLucas Stach static int imx_pd_connector_get_modes(struct drm_connector *connector)
564b6cb2b6SLucas Stach {
574b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = con_to_imxpd(connector);
584b6cb2b6SLucas Stach 	struct device_node *np = imxpd->dev->of_node;
594b6cb2b6SLucas Stach 	int num_modes;
604b6cb2b6SLucas Stach 
614b6cb2b6SLucas Stach 	num_modes = drm_panel_get_modes(imxpd->panel, connector);
624b6cb2b6SLucas Stach 	if (num_modes > 0)
634b6cb2b6SLucas Stach 		return num_modes;
644b6cb2b6SLucas Stach 
654b6cb2b6SLucas Stach 	if (imxpd->edid) {
664b6cb2b6SLucas Stach 		drm_connector_update_edid_property(connector, imxpd->edid);
674b6cb2b6SLucas Stach 		num_modes = drm_add_edid_modes(connector, imxpd->edid);
684b6cb2b6SLucas Stach 	}
694b6cb2b6SLucas Stach 
704b6cb2b6SLucas Stach 	if (np) {
714b6cb2b6SLucas Stach 		struct drm_display_mode *mode = drm_mode_create(connector->dev);
724b6cb2b6SLucas Stach 		int ret;
734b6cb2b6SLucas Stach 
744b6cb2b6SLucas Stach 		if (!mode)
75fd79a093SJani Nikula 			return 0;
764b6cb2b6SLucas Stach 
774b6cb2b6SLucas Stach 		ret = of_get_drm_display_mode(np, &imxpd->mode,
784b6cb2b6SLucas Stach 					      &imxpd->bus_flags,
794b6cb2b6SLucas Stach 					      OF_USE_NATIVE_MODE);
804b6cb2b6SLucas Stach 		if (ret) {
814b6cb2b6SLucas Stach 			drm_mode_destroy(connector->dev, mode);
82fd79a093SJani Nikula 			return 0;
834b6cb2b6SLucas Stach 		}
844b6cb2b6SLucas Stach 
854b6cb2b6SLucas Stach 		drm_mode_copy(mode, &imxpd->mode);
864b6cb2b6SLucas Stach 		mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
874b6cb2b6SLucas Stach 		drm_mode_probed_add(connector, mode);
884b6cb2b6SLucas Stach 		num_modes++;
894b6cb2b6SLucas Stach 	}
904b6cb2b6SLucas Stach 
914b6cb2b6SLucas Stach 	return num_modes;
924b6cb2b6SLucas Stach }
934b6cb2b6SLucas Stach 
imx_pd_bridge_enable(struct drm_bridge * bridge)944b6cb2b6SLucas Stach static void imx_pd_bridge_enable(struct drm_bridge *bridge)
954b6cb2b6SLucas Stach {
964b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
974b6cb2b6SLucas Stach 
984b6cb2b6SLucas Stach 	drm_panel_prepare(imxpd->panel);
994b6cb2b6SLucas Stach 	drm_panel_enable(imxpd->panel);
1004b6cb2b6SLucas Stach }
1014b6cb2b6SLucas Stach 
imx_pd_bridge_disable(struct drm_bridge * bridge)1024b6cb2b6SLucas Stach static void imx_pd_bridge_disable(struct drm_bridge *bridge)
1034b6cb2b6SLucas Stach {
1044b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
1054b6cb2b6SLucas Stach 
1064b6cb2b6SLucas Stach 	drm_panel_disable(imxpd->panel);
1074b6cb2b6SLucas Stach 	drm_panel_unprepare(imxpd->panel);
1084b6cb2b6SLucas Stach }
1094b6cb2b6SLucas Stach 
1104b6cb2b6SLucas Stach static const u32 imx_pd_bus_fmts[] = {
1114b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_RGB888_1X24,
1124b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_BGR888_1X24,
1134b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_GBR888_1X24,
1144b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_RGB666_1X18,
1154b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
1164b6cb2b6SLucas Stach 	MEDIA_BUS_FMT_RGB565_1X16,
1174b6cb2b6SLucas Stach };
1184b6cb2b6SLucas Stach 
1194b6cb2b6SLucas Stach static u32 *
imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state,unsigned int * num_output_fmts)1204b6cb2b6SLucas Stach imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
1214b6cb2b6SLucas Stach 					 struct drm_bridge_state *bridge_state,
1224b6cb2b6SLucas Stach 					 struct drm_crtc_state *crtc_state,
1234b6cb2b6SLucas Stach 					 struct drm_connector_state *conn_state,
1244b6cb2b6SLucas Stach 					 unsigned int *num_output_fmts)
1254b6cb2b6SLucas Stach {
1264b6cb2b6SLucas Stach 	struct drm_display_info *di = &conn_state->connector->display_info;
1274b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
1284b6cb2b6SLucas Stach 	u32 *output_fmts;
1294b6cb2b6SLucas Stach 
1304b6cb2b6SLucas Stach 	if (!imxpd->bus_format && !di->num_bus_formats) {
1314b6cb2b6SLucas Stach 		*num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts);
1324b6cb2b6SLucas Stach 		return kmemdup(imx_pd_bus_fmts, sizeof(imx_pd_bus_fmts),
1334b6cb2b6SLucas Stach 			       GFP_KERNEL);
1344b6cb2b6SLucas Stach 	}
1354b6cb2b6SLucas Stach 
1364b6cb2b6SLucas Stach 	*num_output_fmts = 1;
1374b6cb2b6SLucas Stach 	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
1384b6cb2b6SLucas Stach 	if (!output_fmts)
1394b6cb2b6SLucas Stach 		return NULL;
1404b6cb2b6SLucas Stach 
1414b6cb2b6SLucas Stach 	if (!imxpd->bus_format && di->num_bus_formats)
1424b6cb2b6SLucas Stach 		output_fmts[0] = di->bus_formats[0];
1434b6cb2b6SLucas Stach 	else
1444b6cb2b6SLucas Stach 		output_fmts[0] = imxpd->bus_format;
1454b6cb2b6SLucas Stach 
1464b6cb2b6SLucas Stach 	return output_fmts;
1474b6cb2b6SLucas Stach }
1484b6cb2b6SLucas Stach 
imx_pd_format_supported(u32 output_fmt)1494b6cb2b6SLucas Stach static bool imx_pd_format_supported(u32 output_fmt)
1504b6cb2b6SLucas Stach {
1514b6cb2b6SLucas Stach 	unsigned int i;
1524b6cb2b6SLucas Stach 
1534b6cb2b6SLucas Stach 	for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
1544b6cb2b6SLucas Stach 		if (imx_pd_bus_fmts[i] == output_fmt)
1554b6cb2b6SLucas Stach 			return true;
1564b6cb2b6SLucas Stach 	}
1574b6cb2b6SLucas Stach 
1584b6cb2b6SLucas Stach 	return false;
1594b6cb2b6SLucas Stach }
1604b6cb2b6SLucas Stach 
1614b6cb2b6SLucas Stach static u32 *
imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state,u32 output_fmt,unsigned int * num_input_fmts)1624b6cb2b6SLucas Stach imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
1634b6cb2b6SLucas Stach 					struct drm_bridge_state *bridge_state,
1644b6cb2b6SLucas Stach 					struct drm_crtc_state *crtc_state,
1654b6cb2b6SLucas Stach 					struct drm_connector_state *conn_state,
1664b6cb2b6SLucas Stach 					u32 output_fmt,
1674b6cb2b6SLucas Stach 					unsigned int *num_input_fmts)
1684b6cb2b6SLucas Stach {
1694b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
1704b6cb2b6SLucas Stach 	u32 *input_fmts;
1714b6cb2b6SLucas Stach 
1724b6cb2b6SLucas Stach 	/*
1734b6cb2b6SLucas Stach 	 * If the next bridge does not support bus format negotiation, let's
1744b6cb2b6SLucas Stach 	 * use the static bus format definition (imxpd->bus_format) if it's
1754b6cb2b6SLucas Stach 	 * specified, RGB888 when it's not.
1764b6cb2b6SLucas Stach 	 */
1774b6cb2b6SLucas Stach 	if (output_fmt == MEDIA_BUS_FMT_FIXED)
1784b6cb2b6SLucas Stach 		output_fmt = imxpd->bus_format ? : MEDIA_BUS_FMT_RGB888_1X24;
1794b6cb2b6SLucas Stach 
1804b6cb2b6SLucas Stach 	/* Now make sure the requested output format is supported. */
1814b6cb2b6SLucas Stach 	if ((imxpd->bus_format && imxpd->bus_format != output_fmt) ||
1824b6cb2b6SLucas Stach 	    !imx_pd_format_supported(output_fmt)) {
1834b6cb2b6SLucas Stach 		*num_input_fmts = 0;
1844b6cb2b6SLucas Stach 		return NULL;
1854b6cb2b6SLucas Stach 	}
1864b6cb2b6SLucas Stach 
1874b6cb2b6SLucas Stach 	*num_input_fmts = 1;
1884b6cb2b6SLucas Stach 	input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
1894b6cb2b6SLucas Stach 	if (!input_fmts)
1904b6cb2b6SLucas Stach 		return NULL;
1914b6cb2b6SLucas Stach 
1924b6cb2b6SLucas Stach 	input_fmts[0] = output_fmt;
1934b6cb2b6SLucas Stach 	return input_fmts;
1944b6cb2b6SLucas Stach }
1954b6cb2b6SLucas Stach 
imx_pd_bridge_atomic_check(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)1964b6cb2b6SLucas Stach static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
1974b6cb2b6SLucas Stach 				      struct drm_bridge_state *bridge_state,
1984b6cb2b6SLucas Stach 				      struct drm_crtc_state *crtc_state,
1994b6cb2b6SLucas Stach 				      struct drm_connector_state *conn_state)
2004b6cb2b6SLucas Stach {
2014b6cb2b6SLucas Stach 	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
2024b6cb2b6SLucas Stach 	struct drm_display_info *di = &conn_state->connector->display_info;
2034b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
2044b6cb2b6SLucas Stach 	struct drm_bridge_state *next_bridge_state = NULL;
2054b6cb2b6SLucas Stach 	struct drm_bridge *next_bridge;
2064b6cb2b6SLucas Stach 	u32 bus_flags, bus_fmt;
2074b6cb2b6SLucas Stach 
2084b6cb2b6SLucas Stach 	next_bridge = drm_bridge_get_next_bridge(bridge);
2094b6cb2b6SLucas Stach 	if (next_bridge)
2104b6cb2b6SLucas Stach 		next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
2114b6cb2b6SLucas Stach 								    next_bridge);
2124b6cb2b6SLucas Stach 
2134b6cb2b6SLucas Stach 	if (next_bridge_state)
2144b6cb2b6SLucas Stach 		bus_flags = next_bridge_state->input_bus_cfg.flags;
2154b6cb2b6SLucas Stach 	else if (di->num_bus_formats)
2164b6cb2b6SLucas Stach 		bus_flags = di->bus_flags;
2174b6cb2b6SLucas Stach 	else
2184b6cb2b6SLucas Stach 		bus_flags = imxpd->bus_flags;
2194b6cb2b6SLucas Stach 
2204b6cb2b6SLucas Stach 	bus_fmt = bridge_state->input_bus_cfg.format;
2214b6cb2b6SLucas Stach 	if (!imx_pd_format_supported(bus_fmt))
2224b6cb2b6SLucas Stach 		return -EINVAL;
2234b6cb2b6SLucas Stach 
2244b6cb2b6SLucas Stach 	bridge_state->output_bus_cfg.flags = bus_flags;
2254b6cb2b6SLucas Stach 	bridge_state->input_bus_cfg.flags = bus_flags;
2264b6cb2b6SLucas Stach 	imx_crtc_state->bus_flags = bus_flags;
2274b6cb2b6SLucas Stach 	imx_crtc_state->bus_format = bridge_state->input_bus_cfg.format;
2284b6cb2b6SLucas Stach 	imx_crtc_state->di_hsync_pin = 2;
2294b6cb2b6SLucas Stach 	imx_crtc_state->di_vsync_pin = 3;
2304b6cb2b6SLucas Stach 
2314b6cb2b6SLucas Stach 	return 0;
2324b6cb2b6SLucas Stach }
2334b6cb2b6SLucas Stach 
2344b6cb2b6SLucas Stach static const struct drm_connector_funcs imx_pd_connector_funcs = {
2354b6cb2b6SLucas Stach 	.fill_modes = drm_helper_probe_single_connector_modes,
2364b6cb2b6SLucas Stach 	.destroy = imx_drm_connector_destroy,
2374b6cb2b6SLucas Stach 	.reset = drm_atomic_helper_connector_reset,
2384b6cb2b6SLucas Stach 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
2394b6cb2b6SLucas Stach 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
2404b6cb2b6SLucas Stach };
2414b6cb2b6SLucas Stach 
2424b6cb2b6SLucas Stach static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
2434b6cb2b6SLucas Stach 	.get_modes = imx_pd_connector_get_modes,
2444b6cb2b6SLucas Stach };
2454b6cb2b6SLucas Stach 
2464b6cb2b6SLucas Stach static const struct drm_bridge_funcs imx_pd_bridge_funcs = {
2474b6cb2b6SLucas Stach 	.enable = imx_pd_bridge_enable,
2484b6cb2b6SLucas Stach 	.disable = imx_pd_bridge_disable,
2494b6cb2b6SLucas Stach 	.atomic_reset = drm_atomic_helper_bridge_reset,
2504b6cb2b6SLucas Stach 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
2514b6cb2b6SLucas Stach 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
2524b6cb2b6SLucas Stach 	.atomic_check = imx_pd_bridge_atomic_check,
2534b6cb2b6SLucas Stach 	.atomic_get_input_bus_fmts = imx_pd_bridge_atomic_get_input_bus_fmts,
2544b6cb2b6SLucas Stach 	.atomic_get_output_bus_fmts = imx_pd_bridge_atomic_get_output_bus_fmts,
2554b6cb2b6SLucas Stach };
2564b6cb2b6SLucas Stach 
imx_pd_bind(struct device * dev,struct device * master,void * data)2574b6cb2b6SLucas Stach static int imx_pd_bind(struct device *dev, struct device *master, void *data)
2584b6cb2b6SLucas Stach {
2594b6cb2b6SLucas Stach 	struct drm_device *drm = data;
2604b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
2614b6cb2b6SLucas Stach 	struct imx_parallel_display_encoder *imxpd_encoder;
2624b6cb2b6SLucas Stach 	struct drm_connector *connector;
2634b6cb2b6SLucas Stach 	struct drm_encoder *encoder;
2644b6cb2b6SLucas Stach 	struct drm_bridge *bridge;
2654b6cb2b6SLucas Stach 	int ret;
2664b6cb2b6SLucas Stach 
2674b6cb2b6SLucas Stach 	imxpd_encoder = drmm_simple_encoder_alloc(drm, struct imx_parallel_display_encoder,
2684b6cb2b6SLucas Stach 						  encoder, DRM_MODE_ENCODER_NONE);
2694b6cb2b6SLucas Stach 	if (IS_ERR(imxpd_encoder))
2704b6cb2b6SLucas Stach 		return PTR_ERR(imxpd_encoder);
2714b6cb2b6SLucas Stach 
2724b6cb2b6SLucas Stach 	imxpd_encoder->pd = imxpd;
2734b6cb2b6SLucas Stach 	connector = &imxpd_encoder->connector;
2744b6cb2b6SLucas Stach 	encoder = &imxpd_encoder->encoder;
2754b6cb2b6SLucas Stach 	bridge = &imxpd_encoder->bridge;
2764b6cb2b6SLucas Stach 
2774b6cb2b6SLucas Stach 	ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
2784b6cb2b6SLucas Stach 	if (ret)
2794b6cb2b6SLucas Stach 		return ret;
2804b6cb2b6SLucas Stach 
2814b6cb2b6SLucas Stach 	/* set the connector's dpms to OFF so that
2824b6cb2b6SLucas Stach 	 * drm_helper_connector_dpms() won't return
2834b6cb2b6SLucas Stach 	 * immediately since the current state is ON
2844b6cb2b6SLucas Stach 	 * at this point.
2854b6cb2b6SLucas Stach 	 */
2864b6cb2b6SLucas Stach 	connector->dpms = DRM_MODE_DPMS_OFF;
2874b6cb2b6SLucas Stach 
2884b6cb2b6SLucas Stach 	bridge->funcs = &imx_pd_bridge_funcs;
2894b6cb2b6SLucas Stach 	drm_bridge_attach(encoder, bridge, NULL, 0);
2904b6cb2b6SLucas Stach 
2914b6cb2b6SLucas Stach 	if (imxpd->next_bridge) {
2924b6cb2b6SLucas Stach 		ret = drm_bridge_attach(encoder, imxpd->next_bridge, bridge, 0);
2934b6cb2b6SLucas Stach 		if (ret < 0)
2944b6cb2b6SLucas Stach 			return ret;
2954b6cb2b6SLucas Stach 	} else {
2964b6cb2b6SLucas Stach 		drm_connector_helper_add(connector,
2974b6cb2b6SLucas Stach 					 &imx_pd_connector_helper_funcs);
2984b6cb2b6SLucas Stach 		drm_connector_init(drm, connector, &imx_pd_connector_funcs,
2994b6cb2b6SLucas Stach 				   DRM_MODE_CONNECTOR_DPI);
3004b6cb2b6SLucas Stach 
3014b6cb2b6SLucas Stach 		drm_connector_attach_encoder(connector, encoder);
3024b6cb2b6SLucas Stach 	}
3034b6cb2b6SLucas Stach 
3044b6cb2b6SLucas Stach 	return 0;
3054b6cb2b6SLucas Stach }
3064b6cb2b6SLucas Stach 
3074b6cb2b6SLucas Stach static const struct component_ops imx_pd_ops = {
3084b6cb2b6SLucas Stach 	.bind	= imx_pd_bind,
3094b6cb2b6SLucas Stach };
3104b6cb2b6SLucas Stach 
imx_pd_probe(struct platform_device * pdev)3114b6cb2b6SLucas Stach static int imx_pd_probe(struct platform_device *pdev)
3124b6cb2b6SLucas Stach {
3134b6cb2b6SLucas Stach 	struct device *dev = &pdev->dev;
3144b6cb2b6SLucas Stach 	struct device_node *np = dev->of_node;
3154b6cb2b6SLucas Stach 	const u8 *edidp;
3164b6cb2b6SLucas Stach 	struct imx_parallel_display *imxpd;
3174b6cb2b6SLucas Stach 	int edid_len;
3184b6cb2b6SLucas Stach 	int ret;
3194b6cb2b6SLucas Stach 	u32 bus_format = 0;
3204b6cb2b6SLucas Stach 	const char *fmt;
3214b6cb2b6SLucas Stach 
3224b6cb2b6SLucas Stach 	imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
3234b6cb2b6SLucas Stach 	if (!imxpd)
3244b6cb2b6SLucas Stach 		return -ENOMEM;
3254b6cb2b6SLucas Stach 
3264b6cb2b6SLucas Stach 	/* port@1 is the output port */
3274b6cb2b6SLucas Stach 	ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel,
3284b6cb2b6SLucas Stach 					  &imxpd->next_bridge);
3294b6cb2b6SLucas Stach 	if (ret && ret != -ENODEV)
3304b6cb2b6SLucas Stach 		return ret;
3314b6cb2b6SLucas Stach 
3324b6cb2b6SLucas Stach 	edidp = of_get_property(np, "edid", &edid_len);
3334b6cb2b6SLucas Stach 	if (edidp)
3344b6cb2b6SLucas Stach 		imxpd->edid = devm_kmemdup(dev, edidp, edid_len, GFP_KERNEL);
3354b6cb2b6SLucas Stach 
3364b6cb2b6SLucas Stach 	ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
3374b6cb2b6SLucas Stach 	if (!ret) {
3384b6cb2b6SLucas Stach 		if (!strcmp(fmt, "rgb24"))
3394b6cb2b6SLucas Stach 			bus_format = MEDIA_BUS_FMT_RGB888_1X24;
3404b6cb2b6SLucas Stach 		else if (!strcmp(fmt, "rgb565"))
3414b6cb2b6SLucas Stach 			bus_format = MEDIA_BUS_FMT_RGB565_1X16;
3424b6cb2b6SLucas Stach 		else if (!strcmp(fmt, "bgr666"))
3434b6cb2b6SLucas Stach 			bus_format = MEDIA_BUS_FMT_RGB666_1X18;
3444b6cb2b6SLucas Stach 		else if (!strcmp(fmt, "lvds666"))
3454b6cb2b6SLucas Stach 			bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
3464b6cb2b6SLucas Stach 	}
3474b6cb2b6SLucas Stach 	imxpd->bus_format = bus_format;
3484b6cb2b6SLucas Stach 
3494b6cb2b6SLucas Stach 	imxpd->dev = dev;
3504b6cb2b6SLucas Stach 
3514b6cb2b6SLucas Stach 	platform_set_drvdata(pdev, imxpd);
3524b6cb2b6SLucas Stach 
3534b6cb2b6SLucas Stach 	return component_add(dev, &imx_pd_ops);
3544b6cb2b6SLucas Stach }
3554b6cb2b6SLucas Stach 
imx_pd_remove(struct platform_device * pdev)3564b6cb2b6SLucas Stach static int imx_pd_remove(struct platform_device *pdev)
3574b6cb2b6SLucas Stach {
3584b6cb2b6SLucas Stach 	component_del(&pdev->dev, &imx_pd_ops);
3594b6cb2b6SLucas Stach 
3604b6cb2b6SLucas Stach 	return 0;
3614b6cb2b6SLucas Stach }
3624b6cb2b6SLucas Stach 
3634b6cb2b6SLucas Stach static const struct of_device_id imx_pd_dt_ids[] = {
3644b6cb2b6SLucas Stach 	{ .compatible = "fsl,imx-parallel-display", },
3654b6cb2b6SLucas Stach 	{ /* sentinel */ }
3664b6cb2b6SLucas Stach };
3674b6cb2b6SLucas Stach MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);
3684b6cb2b6SLucas Stach 
3694b6cb2b6SLucas Stach static struct platform_driver imx_pd_driver = {
3704b6cb2b6SLucas Stach 	.probe		= imx_pd_probe,
3714b6cb2b6SLucas Stach 	.remove		= imx_pd_remove,
3724b6cb2b6SLucas Stach 	.driver		= {
3734b6cb2b6SLucas Stach 		.of_match_table = imx_pd_dt_ids,
3744b6cb2b6SLucas Stach 		.name	= "imx-parallel-display",
3754b6cb2b6SLucas Stach 	},
3764b6cb2b6SLucas Stach };
3774b6cb2b6SLucas Stach 
3784b6cb2b6SLucas Stach module_platform_driver(imx_pd_driver);
3794b6cb2b6SLucas Stach 
3804b6cb2b6SLucas Stach MODULE_DESCRIPTION("i.MX parallel display driver");
3814b6cb2b6SLucas Stach MODULE_AUTHOR("Sascha Hauer, Pengutronix");
3824b6cb2b6SLucas Stach MODULE_LICENSE("GPL");
3834b6cb2b6SLucas Stach MODULE_ALIAS("platform:imx-parallel-display");
384