1 //SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 4 * Author: 5 * Sandy Huang <hjc@rock-chips.com> 6 * 7 * This software is licensed under the terms of the GNU General Public 8 * License version 2, as published by the Free Software Foundation, and 9 * may be copied, distributed, and modified under those terms. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <drm/drmP.h> 18 #include <drm/drm_atomic_helper.h> 19 #include <drm/drm_crtc_helper.h> 20 #include <drm/drm_dp_helper.h> 21 #include <drm/drm_panel.h> 22 #include <drm/drm_of.h> 23 24 #include <linux/component.h> 25 #include <linux/of_graph.h> 26 27 #include "rockchip_drm_drv.h" 28 #include "rockchip_drm_vop.h" 29 30 #define encoder_to_rgb(c) container_of(c, struct rockchip_rgb, encoder) 31 32 struct rockchip_rgb { 33 struct device *dev; 34 struct drm_device *drm_dev; 35 struct drm_bridge *bridge; 36 struct drm_encoder encoder; 37 int output_mode; 38 }; 39 40 static int 41 rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder, 42 struct drm_crtc_state *crtc_state, 43 struct drm_connector_state *conn_state) 44 { 45 struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 46 struct drm_connector *connector = conn_state->connector; 47 struct drm_display_info *info = &connector->display_info; 48 u32 bus_format; 49 50 if (info->num_bus_formats) 51 bus_format = info->bus_formats[0]; 52 else 53 bus_format = MEDIA_BUS_FMT_RGB888_1X24; 54 55 switch (bus_format) { 56 case MEDIA_BUS_FMT_RGB666_1X18: 57 s->output_mode = ROCKCHIP_OUT_MODE_P666; 58 break; 59 case MEDIA_BUS_FMT_RGB565_1X16: 60 s->output_mode = ROCKCHIP_OUT_MODE_P565; 61 break; 62 case MEDIA_BUS_FMT_RGB888_1X24: 63 case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: 64 default: 65 s->output_mode = ROCKCHIP_OUT_MODE_P888; 66 break; 67 } 68 69 s->output_type = DRM_MODE_CONNECTOR_LVDS; 70 71 return 0; 72 } 73 74 static const 75 struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = { 76 .atomic_check = rockchip_rgb_encoder_atomic_check, 77 }; 78 79 static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = { 80 .destroy = drm_encoder_cleanup, 81 }; 82 83 struct rockchip_rgb *rockchip_rgb_init(struct device *dev, 84 struct drm_crtc *crtc, 85 struct drm_device *drm_dev) 86 { 87 struct rockchip_rgb *rgb; 88 struct drm_encoder *encoder; 89 struct device_node *port, *endpoint; 90 u32 endpoint_id; 91 int ret = 0, child_count = 0; 92 struct drm_panel *panel; 93 struct drm_bridge *bridge; 94 95 rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL); 96 if (!rgb) 97 return ERR_PTR(-ENOMEM); 98 99 rgb->dev = dev; 100 rgb->drm_dev = drm_dev; 101 102 port = of_graph_get_port_by_id(dev->of_node, 0); 103 if (!port) 104 return ERR_PTR(-EINVAL); 105 106 for_each_child_of_node(port, endpoint) { 107 if (of_property_read_u32(endpoint, "reg", &endpoint_id)) 108 endpoint_id = 0; 109 110 if (rockchip_drm_endpoint_is_subdriver(endpoint) > 0) 111 continue; 112 113 child_count++; 114 ret = drm_of_find_panel_or_bridge(dev->of_node, 0, endpoint_id, 115 &panel, &bridge); 116 if (!ret) 117 break; 118 } 119 120 of_node_put(port); 121 122 /* if the rgb output is not connected to anything, just return */ 123 if (!child_count) 124 return NULL; 125 126 if (ret < 0) { 127 if (ret != -EPROBE_DEFER) 128 DRM_DEV_ERROR(dev, "failed to find panel or bridge %d\n", ret); 129 return ERR_PTR(ret); 130 } 131 132 encoder = &rgb->encoder; 133 encoder->possible_crtcs = drm_crtc_mask(crtc); 134 135 ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs, 136 DRM_MODE_ENCODER_NONE, NULL); 137 if (ret < 0) { 138 DRM_DEV_ERROR(drm_dev->dev, 139 "failed to initialize encoder: %d\n", ret); 140 return ERR_PTR(ret); 141 } 142 143 drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs); 144 145 if (panel) { 146 bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS); 147 if (IS_ERR(bridge)) 148 return ERR_CAST(bridge); 149 } 150 151 rgb->bridge = bridge; 152 153 ret = drm_bridge_attach(encoder, rgb->bridge, NULL); 154 if (ret) { 155 DRM_DEV_ERROR(drm_dev->dev, 156 "failed to attach bridge: %d\n", ret); 157 goto err_free_encoder; 158 } 159 160 return rgb; 161 162 err_free_encoder: 163 drm_encoder_cleanup(encoder); 164 return ERR_PTR(ret); 165 } 166 EXPORT_SYMBOL_GPL(rockchip_rgb_init); 167 168 void rockchip_rgb_fini(struct rockchip_rgb *rgb) 169 { 170 drm_panel_bridge_remove(rgb->bridge); 171 drm_encoder_cleanup(&rgb->encoder); 172 } 173 EXPORT_SYMBOL_GPL(rockchip_rgb_fini); 174