xref: /linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21a396789SBoris Brezillon /*
31a396789SBoris Brezillon  * Copyright (C) 2014 Traphandler
41a396789SBoris Brezillon  * Copyright (C) 2014 Free Electrons
51a396789SBoris Brezillon  * Copyright (C) 2014 Atmel
61a396789SBoris Brezillon  *
71a396789SBoris Brezillon  * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
81a396789SBoris Brezillon  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
91a396789SBoris Brezillon  */
101a396789SBoris Brezillon 
1171866a56SSam Ravnborg #include <linux/media-bus-format.h>
12*73289afeSVille Syrjälä #include <linux/of.h>
131a396789SBoris Brezillon #include <linux/of_graph.h>
141a396789SBoris Brezillon 
15cbb9ea42SThomas Zimmermann #include <drm/drm_bridge.h>
1671866a56SSam Ravnborg #include <drm/drm_encoder.h>
17ebc94461SRob Herring #include <drm/drm_of.h>
18cbb9ea42SThomas Zimmermann #include <drm/drm_simple_kms_helper.h>
191a396789SBoris Brezillon 
201a396789SBoris Brezillon #include "atmel_hlcdc_dc.h"
211a396789SBoris Brezillon 
22b6e075c3SPeter Rosin struct atmel_hlcdc_rgb_output {
23b6e075c3SPeter Rosin 	struct drm_encoder encoder;
24b6e075c3SPeter Rosin 	int bus_fmt;
25b6e075c3SPeter Rosin };
26b6e075c3SPeter Rosin 
27b6e075c3SPeter Rosin static struct atmel_hlcdc_rgb_output *
atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder * encoder)28b6e075c3SPeter Rosin atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)
29b6e075c3SPeter Rosin {
30b6e075c3SPeter Rosin 	return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
31b6e075c3SPeter Rosin }
32b6e075c3SPeter Rosin 
atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder * encoder)33b6e075c3SPeter Rosin int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)
34b6e075c3SPeter Rosin {
35b6e075c3SPeter Rosin 	struct atmel_hlcdc_rgb_output *output;
36b6e075c3SPeter Rosin 
37b6e075c3SPeter Rosin 	output = atmel_hlcdc_encoder_to_rgb_output(encoder);
38b6e075c3SPeter Rosin 
39b6e075c3SPeter Rosin 	return output->bus_fmt;
40b6e075c3SPeter Rosin }
41b6e075c3SPeter Rosin 
atmel_hlcdc_of_bus_fmt(const struct device_node * ep)42b6e075c3SPeter Rosin static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)
43b6e075c3SPeter Rosin {
44b6e075c3SPeter Rosin 	u32 bus_width;
45b6e075c3SPeter Rosin 	int ret;
46b6e075c3SPeter Rosin 
47b6e075c3SPeter Rosin 	ret = of_property_read_u32(ep, "bus-width", &bus_width);
48b6e075c3SPeter Rosin 	if (ret == -EINVAL)
49b6e075c3SPeter Rosin 		return 0;
50b6e075c3SPeter Rosin 	if (ret)
51b6e075c3SPeter Rosin 		return ret;
52b6e075c3SPeter Rosin 
53b6e075c3SPeter Rosin 	switch (bus_width) {
54b6e075c3SPeter Rosin 	case 12:
55b6e075c3SPeter Rosin 		return MEDIA_BUS_FMT_RGB444_1X12;
56b6e075c3SPeter Rosin 	case 16:
57b6e075c3SPeter Rosin 		return MEDIA_BUS_FMT_RGB565_1X16;
58b6e075c3SPeter Rosin 	case 18:
59b6e075c3SPeter Rosin 		return MEDIA_BUS_FMT_RGB666_1X18;
60b6e075c3SPeter Rosin 	case 24:
61b6e075c3SPeter Rosin 		return MEDIA_BUS_FMT_RGB888_1X24;
62b6e075c3SPeter Rosin 	default:
63b6e075c3SPeter Rosin 		return -EINVAL;
64b6e075c3SPeter Rosin 	}
65b6e075c3SPeter Rosin }
66b6e075c3SPeter Rosin 
atmel_hlcdc_attach_endpoint(struct drm_device * dev,int endpoint)676bee9b78SBoris Brezillon static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
6817a8e03eSBoris Brezillon {
69b6e075c3SPeter Rosin 	struct atmel_hlcdc_rgb_output *output;
70b6e075c3SPeter Rosin 	struct device_node *ep;
7117a8e03eSBoris Brezillon 	struct drm_panel *panel;
7217a8e03eSBoris Brezillon 	struct drm_bridge *bridge;
7317a8e03eSBoris Brezillon 	int ret;
7417a8e03eSBoris Brezillon 
75b6e075c3SPeter Rosin 	ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);
76b6e075c3SPeter Rosin 	if (!ep)
77b6e075c3SPeter Rosin 		return -ENODEV;
78b6e075c3SPeter Rosin 
796bee9b78SBoris Brezillon 	ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,
806bee9b78SBoris Brezillon 					  &panel, &bridge);
81b6e075c3SPeter Rosin 	if (ret) {
82b6e075c3SPeter Rosin 		of_node_put(ep);
836bee9b78SBoris Brezillon 		return ret;
84b6e075c3SPeter Rosin 	}
856bee9b78SBoris Brezillon 
86b6e075c3SPeter Rosin 	output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
87b6e075c3SPeter Rosin 	if (!output) {
88b6e075c3SPeter Rosin 		of_node_put(ep);
89b6e075c3SPeter Rosin 		return -ENOMEM;
90b6e075c3SPeter Rosin 	}
91b6e075c3SPeter Rosin 
92b6e075c3SPeter Rosin 	output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
93b6e075c3SPeter Rosin 	of_node_put(ep);
94b6e075c3SPeter Rosin 	if (output->bus_fmt < 0) {
95b6e075c3SPeter Rosin 		dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);
961a396789SBoris Brezillon 		return -EINVAL;
97b6e075c3SPeter Rosin 	}
981a396789SBoris Brezillon 
99cbb9ea42SThomas Zimmermann 	ret = drm_simple_encoder_init(dev, &output->encoder,
100cbb9ea42SThomas Zimmermann 				      DRM_MODE_ENCODER_NONE);
1011a396789SBoris Brezillon 	if (ret)
1021a396789SBoris Brezillon 		return ret;
1031a396789SBoris Brezillon 
104b6e075c3SPeter Rosin 	output->encoder.possible_crtcs = 0x1;
10517a8e03eSBoris Brezillon 
10617a8e03eSBoris Brezillon 	if (panel) {
10789958b7cSLaurent Pinchart 		bridge = drm_panel_bridge_add_typed(panel,
10889958b7cSLaurent Pinchart 						    DRM_MODE_CONNECTOR_Unknown);
10996160a80SEric Anholt 		if (IS_ERR(bridge))
11096160a80SEric Anholt 			return PTR_ERR(bridge);
11117a8e03eSBoris Brezillon 	}
11217a8e03eSBoris Brezillon 
11317a8e03eSBoris Brezillon 	if (bridge) {
114a25b988fSLaurent Pinchart 		ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);
11517a8e03eSBoris Brezillon 		if (!ret)
11617a8e03eSBoris Brezillon 			return 0;
11796160a80SEric Anholt 
11896160a80SEric Anholt 		if (panel)
11996160a80SEric Anholt 			drm_panel_bridge_remove(bridge);
12017a8e03eSBoris Brezillon 	}
1211a396789SBoris Brezillon 
122b6e075c3SPeter Rosin 	drm_encoder_cleanup(&output->encoder);
1231a396789SBoris Brezillon 
1241a396789SBoris Brezillon 	return ret;
1251a396789SBoris Brezillon }
1261a396789SBoris Brezillon 
atmel_hlcdc_create_outputs(struct drm_device * dev)1271a396789SBoris Brezillon int atmel_hlcdc_create_outputs(struct drm_device *dev)
1281a396789SBoris Brezillon {
1296bee9b78SBoris Brezillon 	int endpoint, ret = 0;
130012877b7SPeter Rosin 	int attached = 0;
1311a396789SBoris Brezillon 
132012877b7SPeter Rosin 	/*
133012877b7SPeter Rosin 	 * Always scan the first few endpoints even if we get -ENODEV,
134012877b7SPeter Rosin 	 * but keep going after that as long as we keep getting hits.
135012877b7SPeter Rosin 	 */
136012877b7SPeter Rosin 	for (endpoint = 0; !ret || endpoint < 4; endpoint++) {
1376bee9b78SBoris Brezillon 		ret = atmel_hlcdc_attach_endpoint(dev, endpoint);
138012877b7SPeter Rosin 		if (ret == -ENODEV)
139012877b7SPeter Rosin 			continue;
140012877b7SPeter Rosin 		if (ret)
141012877b7SPeter Rosin 			break;
142012877b7SPeter Rosin 		attached++;
143012877b7SPeter Rosin 	}
1441a396789SBoris Brezillon 
1456bee9b78SBoris Brezillon 	/* At least one device was successfully attached.*/
146012877b7SPeter Rosin 	if (ret == -ENODEV && attached)
1476bee9b78SBoris Brezillon 		return 0;
1481a396789SBoris Brezillon 
14917a8e03eSBoris Brezillon 	return ret;
15017a8e03eSBoris Brezillon }
151