151 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Traphandler4 * Copyright (C) 2014 Free Electrons5 * Copyright (C) 2014 Atmel6 *7 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>8 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>9 */10 11#include <linux/media-bus-format.h>12#include <linux/of.h>13#include <linux/of_graph.h>14 15#include <drm/drm_bridge.h>16#include <drm/drm_encoder.h>17#include <drm/drm_of.h>18#include <drm/drm_simple_kms_helper.h>19 20#include "atmel_hlcdc_dc.h"21 22struct atmel_hlcdc_rgb_output {23 struct drm_encoder encoder;24 int bus_fmt;25};26 27static struct atmel_hlcdc_rgb_output *28atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)29{30 return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);31}32 33int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)34{35 struct atmel_hlcdc_rgb_output *output;36 37 output = atmel_hlcdc_encoder_to_rgb_output(encoder);38 39 return output->bus_fmt;40}41 42static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)43{44 u32 bus_width;45 int ret;46 47 ret = of_property_read_u32(ep, "bus-width", &bus_width);48 if (ret == -EINVAL)49 return 0;50 if (ret)51 return ret;52 53 switch (bus_width) {54 case 12:55 return MEDIA_BUS_FMT_RGB444_1X12;56 case 16:57 return MEDIA_BUS_FMT_RGB565_1X16;58 case 18:59 return MEDIA_BUS_FMT_RGB666_1X18;60 case 24:61 return MEDIA_BUS_FMT_RGB888_1X24;62 default:63 return -EINVAL;64 }65}66 67static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)68{69 struct atmel_hlcdc_rgb_output *output;70 struct device_node *ep;71 struct drm_panel *panel;72 struct drm_bridge *bridge;73 int ret;74 75 ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);76 if (!ep)77 return -ENODEV;78 79 ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,80 &panel, &bridge);81 if (ret) {82 of_node_put(ep);83 return ret;84 }85 86 output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);87 if (!output) {88 of_node_put(ep);89 return -ENOMEM;90 }91 92 output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);93 of_node_put(ep);94 if (output->bus_fmt < 0) {95 dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);96 return -EINVAL;97 }98 99 ret = drm_simple_encoder_init(dev, &output->encoder,100 DRM_MODE_ENCODER_NONE);101 if (ret)102 return ret;103 104 output->encoder.possible_crtcs = 0x1;105 106 if (panel) {107 bridge = drm_panel_bridge_add_typed(panel,108 DRM_MODE_CONNECTOR_Unknown);109 if (IS_ERR(bridge))110 return PTR_ERR(bridge);111 }112 113 if (bridge) {114 ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);115 if (!ret)116 return 0;117 118 if (panel)119 drm_panel_bridge_remove(bridge);120 }121 122 drm_encoder_cleanup(&output->encoder);123 124 return ret;125}126 127int atmel_hlcdc_create_outputs(struct drm_device *dev)128{129 int endpoint, ret = 0;130 int attached = 0;131 132 /*133 * Always scan the first few endpoints even if we get -ENODEV,134 * but keep going after that as long as we keep getting hits.135 */136 for (endpoint = 0; !ret || endpoint < 4; endpoint++) {137 ret = atmel_hlcdc_attach_endpoint(dev, endpoint);138 if (ret == -ENODEV)139 continue;140 if (ret)141 break;142 attached++;143 }144 145 /* At least one device was successfully attached.*/146 if (ret == -ENODEV && attached)147 return 0;148 149 return ret;150}151