11a396789SBoris Brezillon /* 21a396789SBoris Brezillon * Copyright (C) 2014 Traphandler 31a396789SBoris Brezillon * Copyright (C) 2014 Free Electrons 41a396789SBoris Brezillon * Copyright (C) 2014 Atmel 51a396789SBoris Brezillon * 61a396789SBoris Brezillon * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 71a396789SBoris Brezillon * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 81a396789SBoris Brezillon * 91a396789SBoris Brezillon * This program is free software; you can redistribute it and/or modify it 101a396789SBoris Brezillon * under the terms of the GNU General Public License version 2 as published by 111a396789SBoris Brezillon * the Free Software Foundation. 121a396789SBoris Brezillon * 131a396789SBoris Brezillon * This program is distributed in the hope that it will be useful, but WITHOUT 141a396789SBoris Brezillon * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 151a396789SBoris Brezillon * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 161a396789SBoris Brezillon * more details. 171a396789SBoris Brezillon * 181a396789SBoris Brezillon * You should have received a copy of the GNU General Public License along with 191a396789SBoris Brezillon * this program. If not, see <http://www.gnu.org/licenses/>. 201a396789SBoris Brezillon */ 211a396789SBoris Brezillon 221a396789SBoris Brezillon #include <linux/of_graph.h> 231a396789SBoris Brezillon 241a396789SBoris Brezillon #include <drm/drmP.h> 25ebc94461SRob Herring #include <drm/drm_of.h> 2696160a80SEric Anholt #include <drm/drm_bridge.h> 271a396789SBoris Brezillon 281a396789SBoris Brezillon #include "atmel_hlcdc_dc.h" 291a396789SBoris Brezillon 30*b6e075c3SPeter Rosin struct atmel_hlcdc_rgb_output { 31*b6e075c3SPeter Rosin struct drm_encoder encoder; 32*b6e075c3SPeter Rosin int bus_fmt; 33*b6e075c3SPeter Rosin }; 34*b6e075c3SPeter Rosin 351a396789SBoris Brezillon static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { 36510fc3c0SEric Anholt .destroy = drm_encoder_cleanup, 371a396789SBoris Brezillon }; 381a396789SBoris Brezillon 39*b6e075c3SPeter Rosin static struct atmel_hlcdc_rgb_output * 40*b6e075c3SPeter Rosin atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) 41*b6e075c3SPeter Rosin { 42*b6e075c3SPeter Rosin return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); 43*b6e075c3SPeter Rosin } 44*b6e075c3SPeter Rosin 45*b6e075c3SPeter Rosin int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) 46*b6e075c3SPeter Rosin { 47*b6e075c3SPeter Rosin struct atmel_hlcdc_rgb_output *output; 48*b6e075c3SPeter Rosin 49*b6e075c3SPeter Rosin output = atmel_hlcdc_encoder_to_rgb_output(encoder); 50*b6e075c3SPeter Rosin 51*b6e075c3SPeter Rosin return output->bus_fmt; 52*b6e075c3SPeter Rosin } 53*b6e075c3SPeter Rosin 54*b6e075c3SPeter Rosin static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) 55*b6e075c3SPeter Rosin { 56*b6e075c3SPeter Rosin u32 bus_width; 57*b6e075c3SPeter Rosin int ret; 58*b6e075c3SPeter Rosin 59*b6e075c3SPeter Rosin ret = of_property_read_u32(ep, "bus-width", &bus_width); 60*b6e075c3SPeter Rosin if (ret == -EINVAL) 61*b6e075c3SPeter Rosin return 0; 62*b6e075c3SPeter Rosin if (ret) 63*b6e075c3SPeter Rosin return ret; 64*b6e075c3SPeter Rosin 65*b6e075c3SPeter Rosin switch (bus_width) { 66*b6e075c3SPeter Rosin case 12: 67*b6e075c3SPeter Rosin return MEDIA_BUS_FMT_RGB444_1X12; 68*b6e075c3SPeter Rosin case 16: 69*b6e075c3SPeter Rosin return MEDIA_BUS_FMT_RGB565_1X16; 70*b6e075c3SPeter Rosin case 18: 71*b6e075c3SPeter Rosin return MEDIA_BUS_FMT_RGB666_1X18; 72*b6e075c3SPeter Rosin case 24: 73*b6e075c3SPeter Rosin return MEDIA_BUS_FMT_RGB888_1X24; 74*b6e075c3SPeter Rosin default: 75*b6e075c3SPeter Rosin return -EINVAL; 76*b6e075c3SPeter Rosin } 77*b6e075c3SPeter Rosin } 78*b6e075c3SPeter Rosin 796bee9b78SBoris Brezillon static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) 8017a8e03eSBoris Brezillon { 81*b6e075c3SPeter Rosin struct atmel_hlcdc_rgb_output *output; 82*b6e075c3SPeter Rosin struct device_node *ep; 8317a8e03eSBoris Brezillon struct drm_panel *panel; 8417a8e03eSBoris Brezillon struct drm_bridge *bridge; 8517a8e03eSBoris Brezillon int ret; 8617a8e03eSBoris Brezillon 87*b6e075c3SPeter Rosin ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); 88*b6e075c3SPeter Rosin if (!ep) 89*b6e075c3SPeter Rosin return -ENODEV; 90*b6e075c3SPeter Rosin 916bee9b78SBoris Brezillon ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, 926bee9b78SBoris Brezillon &panel, &bridge); 93*b6e075c3SPeter Rosin if (ret) { 94*b6e075c3SPeter Rosin of_node_put(ep); 956bee9b78SBoris Brezillon return ret; 96*b6e075c3SPeter Rosin } 976bee9b78SBoris Brezillon 98*b6e075c3SPeter Rosin output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); 99*b6e075c3SPeter Rosin if (!output) { 100*b6e075c3SPeter Rosin of_node_put(ep); 101*b6e075c3SPeter Rosin return -ENOMEM; 102*b6e075c3SPeter Rosin } 103*b6e075c3SPeter Rosin 104*b6e075c3SPeter Rosin output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); 105*b6e075c3SPeter Rosin of_node_put(ep); 106*b6e075c3SPeter Rosin if (output->bus_fmt < 0) { 107*b6e075c3SPeter Rosin dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); 1081a396789SBoris Brezillon return -EINVAL; 109*b6e075c3SPeter Rosin } 1101a396789SBoris Brezillon 111*b6e075c3SPeter Rosin ret = drm_encoder_init(dev, &output->encoder, 1121a396789SBoris Brezillon &atmel_hlcdc_panel_encoder_funcs, 113abfc936eSBoris Brezillon DRM_MODE_ENCODER_NONE, NULL); 1141a396789SBoris Brezillon if (ret) 1151a396789SBoris Brezillon return ret; 1161a396789SBoris Brezillon 117*b6e075c3SPeter Rosin output->encoder.possible_crtcs = 0x1; 11817a8e03eSBoris Brezillon 11917a8e03eSBoris Brezillon if (panel) { 12096160a80SEric Anholt bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); 12196160a80SEric Anholt if (IS_ERR(bridge)) 12296160a80SEric Anholt return PTR_ERR(bridge); 12317a8e03eSBoris Brezillon } 12417a8e03eSBoris Brezillon 12517a8e03eSBoris Brezillon if (bridge) { 126*b6e075c3SPeter Rosin ret = drm_bridge_attach(&output->encoder, bridge, NULL); 12717a8e03eSBoris Brezillon if (!ret) 12817a8e03eSBoris Brezillon return 0; 12996160a80SEric Anholt 13096160a80SEric Anholt if (panel) 13196160a80SEric Anholt drm_panel_bridge_remove(bridge); 13217a8e03eSBoris Brezillon } 1331a396789SBoris Brezillon 134*b6e075c3SPeter Rosin drm_encoder_cleanup(&output->encoder); 1351a396789SBoris Brezillon 1361a396789SBoris Brezillon return ret; 1371a396789SBoris Brezillon } 1381a396789SBoris Brezillon 1391a396789SBoris Brezillon int atmel_hlcdc_create_outputs(struct drm_device *dev) 1401a396789SBoris Brezillon { 1416bee9b78SBoris Brezillon int endpoint, ret = 0; 142012877b7SPeter Rosin int attached = 0; 1431a396789SBoris Brezillon 144012877b7SPeter Rosin /* 145012877b7SPeter Rosin * Always scan the first few endpoints even if we get -ENODEV, 146012877b7SPeter Rosin * but keep going after that as long as we keep getting hits. 147012877b7SPeter Rosin */ 148012877b7SPeter Rosin for (endpoint = 0; !ret || endpoint < 4; endpoint++) { 1496bee9b78SBoris Brezillon ret = atmel_hlcdc_attach_endpoint(dev, endpoint); 150012877b7SPeter Rosin if (ret == -ENODEV) 151012877b7SPeter Rosin continue; 152012877b7SPeter Rosin if (ret) 153012877b7SPeter Rosin break; 154012877b7SPeter Rosin attached++; 155012877b7SPeter Rosin } 1561a396789SBoris Brezillon 1576bee9b78SBoris Brezillon /* At least one device was successfully attached.*/ 158012877b7SPeter Rosin if (ret == -ENODEV && attached) 1596bee9b78SBoris Brezillon return 0; 1601a396789SBoris Brezillon 16117a8e03eSBoris Brezillon return ret; 16217a8e03eSBoris Brezillon } 163