brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · 91d7808 Raw
386 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * i.MX drm driver - parallel display implementation4 *5 * Copyright (C) 2012 Sascha Hauer, Pengutronix6 */7 8#include <linux/component.h>9#include <linux/media-bus-format.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/platform_device.h>13#include <linux/videodev2.h>14 15#include <video/of_display_timing.h>16 17#include <drm/drm_atomic_helper.h>18#include <drm/drm_bridge.h>19#include <drm/drm_edid.h>20#include <drm/drm_managed.h>21#include <drm/drm_of.h>22#include <drm/drm_panel.h>23#include <drm/drm_probe_helper.h>24#include <drm/drm_simple_kms_helper.h>25 26#include "imx-drm.h"27 28struct imx_parallel_display_encoder {29	struct drm_connector connector;30	struct drm_encoder encoder;31	struct drm_bridge bridge;32	struct imx_parallel_display *pd;33};34 35struct imx_parallel_display {36	struct device *dev;37	const struct drm_edid *drm_edid;38	u32 bus_format;39	u32 bus_flags;40	struct drm_display_mode mode;41	struct drm_panel *panel;42	struct drm_bridge *next_bridge;43};44 45static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)46{47	return container_of(c, struct imx_parallel_display_encoder, connector)->pd;48}49 50static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b)51{52	return container_of(b, struct imx_parallel_display_encoder, bridge)->pd;53}54 55static int imx_pd_connector_get_modes(struct drm_connector *connector)56{57	struct imx_parallel_display *imxpd = con_to_imxpd(connector);58	struct device_node *np = imxpd->dev->of_node;59	int num_modes;60 61	num_modes = drm_panel_get_modes(imxpd->panel, connector);62	if (num_modes > 0)63		return num_modes;64 65	if (imxpd->drm_edid) {66		drm_edid_connector_update(connector, imxpd->drm_edid);67		num_modes = drm_edid_connector_add_modes(connector);68	}69 70	if (np) {71		struct drm_display_mode *mode = drm_mode_create(connector->dev);72		int ret;73 74		if (!mode)75			return 0;76 77		ret = of_get_drm_display_mode(np, &imxpd->mode,78					      &imxpd->bus_flags,79					      OF_USE_NATIVE_MODE);80		if (ret) {81			drm_mode_destroy(connector->dev, mode);82			return 0;83		}84 85		drm_mode_copy(mode, &imxpd->mode);86		mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;87		drm_mode_probed_add(connector, mode);88		num_modes++;89	}90 91	return num_modes;92}93 94static void imx_pd_bridge_enable(struct drm_bridge *bridge)95{96	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);97 98	drm_panel_prepare(imxpd->panel);99	drm_panel_enable(imxpd->panel);100}101 102static void imx_pd_bridge_disable(struct drm_bridge *bridge)103{104	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);105 106	drm_panel_disable(imxpd->panel);107	drm_panel_unprepare(imxpd->panel);108}109 110static const u32 imx_pd_bus_fmts[] = {111	MEDIA_BUS_FMT_RGB888_1X24,112	MEDIA_BUS_FMT_BGR888_1X24,113	MEDIA_BUS_FMT_GBR888_1X24,114	MEDIA_BUS_FMT_RGB666_1X18,115	MEDIA_BUS_FMT_RGB666_1X24_CPADHI,116	MEDIA_BUS_FMT_RGB565_1X16,117};118 119static u32 *120imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,121					 struct drm_bridge_state *bridge_state,122					 struct drm_crtc_state *crtc_state,123					 struct drm_connector_state *conn_state,124					 unsigned int *num_output_fmts)125{126	struct drm_display_info *di = &conn_state->connector->display_info;127	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);128	u32 *output_fmts;129 130	if (!imxpd->bus_format && !di->num_bus_formats) {131		*num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts);132		return kmemdup(imx_pd_bus_fmts, sizeof(imx_pd_bus_fmts),133			       GFP_KERNEL);134	}135 136	*num_output_fmts = 1;137	output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);138	if (!output_fmts)139		return NULL;140 141	if (!imxpd->bus_format && di->num_bus_formats)142		output_fmts[0] = di->bus_formats[0];143	else144		output_fmts[0] = imxpd->bus_format;145 146	return output_fmts;147}148 149static bool imx_pd_format_supported(u32 output_fmt)150{151	unsigned int i;152 153	for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {154		if (imx_pd_bus_fmts[i] == output_fmt)155			return true;156	}157 158	return false;159}160 161static u32 *162imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,163					struct drm_bridge_state *bridge_state,164					struct drm_crtc_state *crtc_state,165					struct drm_connector_state *conn_state,166					u32 output_fmt,167					unsigned int *num_input_fmts)168{169	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);170	u32 *input_fmts;171 172	/*173	 * If the next bridge does not support bus format negotiation, let's174	 * use the static bus format definition (imxpd->bus_format) if it's175	 * specified, RGB888 when it's not.176	 */177	if (output_fmt == MEDIA_BUS_FMT_FIXED)178		output_fmt = imxpd->bus_format ? : MEDIA_BUS_FMT_RGB888_1X24;179 180	/* Now make sure the requested output format is supported. */181	if ((imxpd->bus_format && imxpd->bus_format != output_fmt) ||182	    !imx_pd_format_supported(output_fmt)) {183		*num_input_fmts = 0;184		return NULL;185	}186 187	*num_input_fmts = 1;188	input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);189	if (!input_fmts)190		return NULL;191 192	input_fmts[0] = output_fmt;193	return input_fmts;194}195 196static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,197				      struct drm_bridge_state *bridge_state,198				      struct drm_crtc_state *crtc_state,199				      struct drm_connector_state *conn_state)200{201	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);202	struct drm_display_info *di = &conn_state->connector->display_info;203	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);204	struct drm_bridge_state *next_bridge_state = NULL;205	struct drm_bridge *next_bridge;206	u32 bus_flags, bus_fmt;207 208	next_bridge = drm_bridge_get_next_bridge(bridge);209	if (next_bridge)210		next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,211								    next_bridge);212 213	if (next_bridge_state)214		bus_flags = next_bridge_state->input_bus_cfg.flags;215	else if (di->num_bus_formats)216		bus_flags = di->bus_flags;217	else218		bus_flags = imxpd->bus_flags;219 220	bus_fmt = bridge_state->input_bus_cfg.format;221	if (!imx_pd_format_supported(bus_fmt))222		return -EINVAL;223 224	bridge_state->output_bus_cfg.flags = bus_flags;225	bridge_state->input_bus_cfg.flags = bus_flags;226	imx_crtc_state->bus_flags = bus_flags;227	imx_crtc_state->bus_format = bridge_state->input_bus_cfg.format;228	imx_crtc_state->di_hsync_pin = 2;229	imx_crtc_state->di_vsync_pin = 3;230 231	return 0;232}233 234static const struct drm_connector_funcs imx_pd_connector_funcs = {235	.fill_modes = drm_helper_probe_single_connector_modes,236	.destroy = imx_drm_connector_destroy,237	.reset = drm_atomic_helper_connector_reset,238	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,239	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,240};241 242static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {243	.get_modes = imx_pd_connector_get_modes,244};245 246static const struct drm_bridge_funcs imx_pd_bridge_funcs = {247	.enable = imx_pd_bridge_enable,248	.disable = imx_pd_bridge_disable,249	.atomic_reset = drm_atomic_helper_bridge_reset,250	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,251	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,252	.atomic_check = imx_pd_bridge_atomic_check,253	.atomic_get_input_bus_fmts = imx_pd_bridge_atomic_get_input_bus_fmts,254	.atomic_get_output_bus_fmts = imx_pd_bridge_atomic_get_output_bus_fmts,255};256 257static int imx_pd_bind(struct device *dev, struct device *master, void *data)258{259	struct drm_device *drm = data;260	struct imx_parallel_display *imxpd = dev_get_drvdata(dev);261	struct imx_parallel_display_encoder *imxpd_encoder;262	struct drm_connector *connector;263	struct drm_encoder *encoder;264	struct drm_bridge *bridge;265	int ret;266 267	imxpd_encoder = drmm_simple_encoder_alloc(drm, struct imx_parallel_display_encoder,268						  encoder, DRM_MODE_ENCODER_NONE);269	if (IS_ERR(imxpd_encoder))270		return PTR_ERR(imxpd_encoder);271 272	imxpd_encoder->pd = imxpd;273	connector = &imxpd_encoder->connector;274	encoder = &imxpd_encoder->encoder;275	bridge = &imxpd_encoder->bridge;276 277	ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);278	if (ret)279		return ret;280 281	/* set the connector's dpms to OFF so that282	 * drm_helper_connector_dpms() won't return283	 * immediately since the current state is ON284	 * at this point.285	 */286	connector->dpms = DRM_MODE_DPMS_OFF;287 288	bridge->funcs = &imx_pd_bridge_funcs;289	drm_bridge_attach(encoder, bridge, NULL, 0);290 291	if (imxpd->next_bridge) {292		ret = drm_bridge_attach(encoder, imxpd->next_bridge, bridge, 0);293		if (ret < 0)294			return ret;295	} else {296		drm_connector_helper_add(connector,297					 &imx_pd_connector_helper_funcs);298		drm_connector_init(drm, connector, &imx_pd_connector_funcs,299				   DRM_MODE_CONNECTOR_DPI);300 301		drm_connector_attach_encoder(connector, encoder);302	}303 304	return 0;305}306 307static const struct component_ops imx_pd_ops = {308	.bind	= imx_pd_bind,309};310 311static int imx_pd_probe(struct platform_device *pdev)312{313	struct device *dev = &pdev->dev;314	struct device_node *np = dev->of_node;315	const u8 *edidp;316	struct imx_parallel_display *imxpd;317	int edid_len;318	int ret;319	u32 bus_format = 0;320	const char *fmt;321 322	imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);323	if (!imxpd)324		return -ENOMEM;325 326	/* port@1 is the output port */327	ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel,328					  &imxpd->next_bridge);329	if (ret && ret != -ENODEV)330		return ret;331 332	edidp = of_get_property(np, "edid", &edid_len);333	if (edidp)334		imxpd->drm_edid = drm_edid_alloc(edidp, edid_len);335 336	ret = of_property_read_string(np, "interface-pix-fmt", &fmt);337	if (!ret) {338		if (!strcmp(fmt, "rgb24"))339			bus_format = MEDIA_BUS_FMT_RGB888_1X24;340		else if (!strcmp(fmt, "rgb565"))341			bus_format = MEDIA_BUS_FMT_RGB565_1X16;342		else if (!strcmp(fmt, "bgr666"))343			bus_format = MEDIA_BUS_FMT_RGB666_1X18;344		else if (!strcmp(fmt, "lvds666"))345			bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;346	}347	imxpd->bus_format = bus_format;348 349	imxpd->dev = dev;350 351	platform_set_drvdata(pdev, imxpd);352 353	return component_add(dev, &imx_pd_ops);354}355 356static void imx_pd_remove(struct platform_device *pdev)357{358	struct imx_parallel_display *imxpd = platform_get_drvdata(pdev);359 360	component_del(&pdev->dev, &imx_pd_ops);361 362	drm_edid_free(imxpd->drm_edid);363}364 365static const struct of_device_id imx_pd_dt_ids[] = {366	{ .compatible = "fsl,imx-parallel-display", },367	{ /* sentinel */ }368};369MODULE_DEVICE_TABLE(of, imx_pd_dt_ids);370 371static struct platform_driver imx_pd_driver = {372	.probe		= imx_pd_probe,373	.remove_new	= imx_pd_remove,374	.driver		= {375		.of_match_table = imx_pd_dt_ids,376		.name	= "imx-parallel-display",377	},378};379 380module_platform_driver(imx_pd_driver);381 382MODULE_DESCRIPTION("i.MX parallel display driver");383MODULE_AUTHOR("Sascha Hauer, Pengutronix");384MODULE_LICENSE("GPL");385MODULE_ALIAS("platform:imx-parallel-display");386