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