Path: blob/master/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
26494 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2014 Traphandler3* Copyright (C) 2014 Free Electrons4* Copyright (C) 2014 Atmel5*6* Author: Jean-Jacques Hiblot <[email protected]>7* Author: Boris BREZILLON <[email protected]>8*/910#include <linux/media-bus-format.h>11#include <linux/of.h>12#include <linux/of_graph.h>1314#include <drm/drm_bridge.h>15#include <drm/drm_encoder.h>16#include <drm/drm_of.h>17#include <drm/drm_simple_kms_helper.h>1819#include "atmel_hlcdc_dc.h"2021struct atmel_hlcdc_rgb_output {22struct drm_encoder encoder;23int bus_fmt;24};2526static struct atmel_hlcdc_rgb_output *27atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder)28{29return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);30}3132int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder)33{34struct atmel_hlcdc_rgb_output *output;3536output = atmel_hlcdc_encoder_to_rgb_output(encoder);3738return output->bus_fmt;39}4041static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep)42{43u32 bus_width;44int ret;4546ret = of_property_read_u32(ep, "bus-width", &bus_width);47if (ret == -EINVAL)48return 0;49if (ret)50return ret;5152switch (bus_width) {53case 12:54return MEDIA_BUS_FMT_RGB444_1X12;55case 16:56return MEDIA_BUS_FMT_RGB565_1X16;57case 18:58return MEDIA_BUS_FMT_RGB666_1X18;59case 24:60return MEDIA_BUS_FMT_RGB888_1X24;61default:62return -EINVAL;63}64}6566static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)67{68struct atmel_hlcdc_rgb_output *output;69struct device_node *ep;70struct drm_panel *panel;71struct drm_bridge *bridge;72int ret;7374ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint);75if (!ep)76return -ENODEV;7778ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint,79&panel, &bridge);80if (ret) {81of_node_put(ep);82return ret;83}8485output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);86if (!output) {87of_node_put(ep);88return -ENOMEM;89}9091output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);92of_node_put(ep);93if (output->bus_fmt < 0) {94dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);95return -EINVAL;96}9798ret = drm_simple_encoder_init(dev, &output->encoder,99DRM_MODE_ENCODER_NONE);100if (ret)101return ret;102103output->encoder.possible_crtcs = 0x1;104105if (panel) {106bridge = drm_panel_bridge_add_typed(panel,107DRM_MODE_CONNECTOR_Unknown);108if (IS_ERR(bridge))109return PTR_ERR(bridge);110}111112if (bridge) {113ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0);114if (!ret)115return 0;116117if (panel)118drm_panel_bridge_remove(bridge);119}120121drm_encoder_cleanup(&output->encoder);122123return ret;124}125126int atmel_hlcdc_create_outputs(struct drm_device *dev)127{128int endpoint, ret = 0;129int attached = 0;130131/*132* Always scan the first few endpoints even if we get -ENODEV,133* but keep going after that as long as we keep getting hits.134*/135for (endpoint = 0; !ret || endpoint < 4; endpoint++) {136ret = atmel_hlcdc_attach_endpoint(dev, endpoint);137if (ret == -ENODEV)138continue;139if (ret)140break;141attached++;142}143144/* At least one device was successfully attached.*/145if (ret == -ENODEV && attached)146return 0;147148return ret;149}150151152