Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'du-next-20191218' of git://linuxtv.org/pinchartl/media into drm-next

R-Car Display Unit changes:

- Color Management Module support
- LVDS encoder dual-link support enhancements
- R8A77980 support

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191218151710.GA13830@pendragon.ideasonboard.com

+856 -155
+67
Documentation/devicetree/bindings/display/renesas,cmm.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/display/renesas,cmm.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Renesas R-Car Color Management Module (CMM) 8 + 9 + maintainers: 10 + - Laurent Pinchart <laurent.pinchart@ideasonboard.com> 11 + - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> 12 + - Jacopo Mondi <jacopo+renesas@jmondi.org> 13 + 14 + description: |+ 15 + Renesas R-Car color management module connected to R-Car DU video channels. 16 + It provides image enhancement functions such as 1-D look-up tables (LUT), 17 + 3-D look-up tables (CLU), 1D-histogram generation (HGO), and color 18 + space conversion (CSC). 19 + 20 + properties: 21 + compatible: 22 + oneOf: 23 + - items: 24 + - enum: 25 + - renesas,r8a7795-cmm 26 + - renesas,r8a7796-cmm 27 + - renesas,r8a77965-cmm 28 + - renesas,r8a77990-cmm 29 + - renesas,r8a77995-cmm 30 + - const: renesas,rcar-gen3-cmm 31 + - items: 32 + - const: renesas,rcar-gen2-cmm 33 + 34 + reg: 35 + maxItems: 1 36 + 37 + clocks: 38 + maxItems: 1 39 + 40 + resets: 41 + maxItems: 1 42 + 43 + power-domains: 44 + maxItems: 1 45 + 46 + required: 47 + - compatible 48 + - reg 49 + - clocks 50 + - resets 51 + - power-domains 52 + 53 + additionalProperties: false 54 + 55 + examples: 56 + - | 57 + #include <dt-bindings/clock/r8a7796-cpg-mssr.h> 58 + #include <dt-bindings/power/r8a7796-sysc.h> 59 + 60 + cmm0: cmm@fea40000 { 61 + compatible = "renesas,r8a7796-cmm", 62 + "renesas,rcar-gen3-cmm"; 63 + reg = <0 0xfea40000 0 0x1000>; 64 + power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; 65 + clocks = <&cpg CPG_MOD 711>; 66 + resets = <&cpg 711>; 67 + };
+10 -5
Documentation/devicetree/bindings/display/renesas,du.txt
··· 41 41 supplied they must be named "dclkin.x" with "x" being the input clock 42 42 numerical index. 43 43 44 - - vsps: A list of phandle and channel index tuples to the VSPs that handle 45 - the memory interfaces for the DU channels. The phandle identifies the VSP 46 - instance that serves the DU channel, and the channel index identifies the 47 - LIF instance in that VSP. 44 + - renesas,cmms: A list of phandles to the CMM instances present in the SoC, 45 + one for each available DU channel. The property shall not be specified for 46 + SoCs that do not provide any CMM (such as V3M and V3H). 47 + 48 + - renesas,vsps: A list of phandle and channel index tuples to the VSPs that 49 + handle the memory interfaces for the DU channels. The phandle identifies the 50 + VSP instance that serves the DU channel, and the channel index identifies 51 + the LIF instance in that VSP. 48 52 49 53 Required nodes: 50 54 ··· 96 92 <&cpg CPG_MOD 722>, 97 93 <&cpg CPG_MOD 721>; 98 94 clock-names = "du.0", "du.1", "du.2", "du.3"; 99 - vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>; 95 + renesas,cmms = <&cmm0>, <&cmm1>, <&cmm2>, <&cmm3>; 96 + renesas,vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>; 100 97 101 98 ports { 102 99 #address-cells = <1>;
+116
drivers/gpu/drm/drm_of.c
··· 274 274 return ret; 275 275 } 276 276 EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); 277 + 278 + enum drm_of_lvds_pixels { 279 + DRM_OF_LVDS_EVEN = BIT(0), 280 + DRM_OF_LVDS_ODD = BIT(1), 281 + }; 282 + 283 + static int drm_of_lvds_get_port_pixels_type(struct device_node *port_node) 284 + { 285 + bool even_pixels = 286 + of_property_read_bool(port_node, "dual-lvds-even-pixels"); 287 + bool odd_pixels = 288 + of_property_read_bool(port_node, "dual-lvds-odd-pixels"); 289 + 290 + return (even_pixels ? DRM_OF_LVDS_EVEN : 0) | 291 + (odd_pixels ? DRM_OF_LVDS_ODD : 0); 292 + } 293 + 294 + static int drm_of_lvds_get_remote_pixels_type( 295 + const struct device_node *port_node) 296 + { 297 + struct device_node *endpoint = NULL; 298 + int pixels_type = -EPIPE; 299 + 300 + for_each_child_of_node(port_node, endpoint) { 301 + struct device_node *remote_port; 302 + int current_pt; 303 + 304 + if (!of_node_name_eq(endpoint, "endpoint")) 305 + continue; 306 + 307 + remote_port = of_graph_get_remote_port(endpoint); 308 + if (!remote_port) { 309 + of_node_put(remote_port); 310 + return -EPIPE; 311 + } 312 + 313 + current_pt = drm_of_lvds_get_port_pixels_type(remote_port); 314 + of_node_put(remote_port); 315 + if (pixels_type < 0) 316 + pixels_type = current_pt; 317 + 318 + /* 319 + * Sanity check, ensure that all remote endpoints have the same 320 + * pixel type. We may lift this restriction later if we need to 321 + * support multiple sinks with different dual-link 322 + * configurations by passing the endpoints explicitly to 323 + * drm_of_lvds_get_dual_link_pixel_order(). 324 + */ 325 + if (!current_pt || pixels_type != current_pt) { 326 + of_node_put(remote_port); 327 + return -EINVAL; 328 + } 329 + } 330 + 331 + return pixels_type; 332 + } 333 + 334 + /** 335 + * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order 336 + * @port1: First DT port node of the Dual-link LVDS source 337 + * @port2: Second DT port node of the Dual-link LVDS source 338 + * 339 + * An LVDS dual-link connection is made of two links, with even pixels 340 + * transitting on one link, and odd pixels on the other link. This function 341 + * returns, for two ports of an LVDS dual-link source, which port shall transmit 342 + * the even and odd pixels, based on the requirements of the connected sink. 343 + * 344 + * The pixel order is determined from the dual-lvds-even-pixels and 345 + * dual-lvds-odd-pixels properties in the sink's DT port nodes. If those 346 + * properties are not present, or if their usage is not valid, this function 347 + * returns -EINVAL. 348 + * 349 + * If either port is not connected, this function returns -EPIPE. 350 + * 351 + * @port1 and @port2 are typically DT sibling nodes, but may have different 352 + * parents when, for instance, two separate LVDS encoders carry the even and odd 353 + * pixels. 354 + * 355 + * Return: 356 + * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2 357 + * carries odd pixels 358 + * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2 359 + * carries even pixels 360 + * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or 361 + * the sink configuration is invalid 362 + * * -EPIPE - when @port1 or @port2 are not connected 363 + */ 364 + int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, 365 + const struct device_node *port2) 366 + { 367 + int remote_p1_pt, remote_p2_pt; 368 + 369 + if (!port1 || !port2) 370 + return -EINVAL; 371 + 372 + remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1); 373 + if (remote_p1_pt < 0) 374 + return remote_p1_pt; 375 + 376 + remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2); 377 + if (remote_p2_pt < 0) 378 + return remote_p2_pt; 379 + 380 + /* 381 + * A valid dual-lVDS bus is found when one remote port is marked with 382 + * "dual-lvds-even-pixels", and the other remote port is marked with 383 + * "dual-lvds-odd-pixels", bail out if the markers are not right. 384 + */ 385 + if (remote_p1_pt + remote_p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD) 386 + return -EINVAL; 387 + 388 + return remote_p1_pt == DRM_OF_LVDS_EVEN ? 389 + DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS : 390 + DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; 391 + } 392 + EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order);
+8
drivers/gpu/drm/rcar-du/Kconfig
··· 4 4 depends on DRM && OF 5 5 depends on ARM || ARM64 6 6 depends on ARCH_RENESAS || COMPILE_TEST 7 + imply DRM_RCAR_CMM 7 8 imply DRM_RCAR_LVDS 8 9 select DRM_KMS_HELPER 9 10 select DRM_KMS_CMA_HELPER ··· 13 12 help 14 13 Choose this option if you have an R-Car chipset. 15 14 If M is selected the module will be called rcar-du-drm. 15 + 16 + config DRM_RCAR_CMM 17 + tristate "R-Car DU Color Management Module (CMM) Support" 18 + depends on DRM && OF 19 + depends on DRM_RCAR_DU 20 + help 21 + Enable support for R-Car Color Management Module (CMM). 16 22 17 23 config DRM_RCAR_DW_HDMI 18 24 tristate "R-Car DU Gen3 HDMI Encoder Support"
+1
drivers/gpu/drm/rcar-du/Makefile
··· 15 15 rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o 16 16 rcar-du-drm-$(CONFIG_DRM_RCAR_WRITEBACK) += rcar_du_writeback.o 17 17 18 + obj-$(CONFIG_DRM_RCAR_CMM) += rcar_cmm.o 18 19 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 19 20 obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o 20 21 obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
+217
drivers/gpu/drm/rcar-du/rcar_cmm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * rcar_cmm.c -- R-Car Display Unit Color Management Module 4 + * 5 + * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org> 6 + */ 7 + 8 + #include <linux/io.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/pm_runtime.h> 13 + 14 + #include <drm/drm_color_mgmt.h> 15 + 16 + #include "rcar_cmm.h" 17 + 18 + #define CM2_LUT_CTRL 0x0000 19 + #define CM2_LUT_CTRL_LUT_EN BIT(0) 20 + #define CM2_LUT_TBL_BASE 0x0600 21 + #define CM2_LUT_TBL(__i) (CM2_LUT_TBL_BASE + (__i) * 4) 22 + 23 + struct rcar_cmm { 24 + void __iomem *base; 25 + 26 + /* 27 + * @lut: 1D-LUT state 28 + * @lut.enabled: 1D-LUT enabled flag 29 + */ 30 + struct { 31 + bool enabled; 32 + } lut; 33 + }; 34 + 35 + static inline int rcar_cmm_read(struct rcar_cmm *rcmm, u32 reg) 36 + { 37 + return ioread32(rcmm->base + reg); 38 + } 39 + 40 + static inline void rcar_cmm_write(struct rcar_cmm *rcmm, u32 reg, u32 data) 41 + { 42 + iowrite32(data, rcmm->base + reg); 43 + } 44 + 45 + /* 46 + * rcar_cmm_lut_write() - Scale the DRM LUT table entries to hardware precision 47 + * and write to the CMM registers 48 + * @rcmm: Pointer to the CMM device 49 + * @drm_lut: Pointer to the DRM LUT table 50 + */ 51 + static void rcar_cmm_lut_write(struct rcar_cmm *rcmm, 52 + const struct drm_color_lut *drm_lut) 53 + { 54 + unsigned int i; 55 + 56 + for (i = 0; i < CM2_LUT_SIZE; ++i) { 57 + u32 entry = drm_color_lut_extract(drm_lut[i].red, 8) << 16 58 + | drm_color_lut_extract(drm_lut[i].green, 8) << 8 59 + | drm_color_lut_extract(drm_lut[i].blue, 8); 60 + 61 + rcar_cmm_write(rcmm, CM2_LUT_TBL(i), entry); 62 + } 63 + } 64 + 65 + /* 66 + * rcar_cmm_setup() - Configure the CMM unit 67 + * @pdev: The platform device associated with the CMM instance 68 + * @config: The CMM unit configuration 69 + * 70 + * Configure the CMM unit with the given configuration. Currently enabling, 71 + * disabling and programming of the 1-D LUT unit is supported. 72 + * 73 + * As rcar_cmm_setup() accesses the CMM registers the unit should be powered 74 + * and its functional clock enabled. To guarantee this, before any call to 75 + * this function is made, the CMM unit has to be enabled by calling 76 + * rcar_cmm_enable() first. 77 + * 78 + * TODO: Add support for LUT double buffer operations to avoid updating the 79 + * LUT table entries while a frame is being displayed. 80 + */ 81 + int rcar_cmm_setup(struct platform_device *pdev, 82 + const struct rcar_cmm_config *config) 83 + { 84 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 85 + 86 + /* Disable LUT if no table is provided. */ 87 + if (!config->lut.table) { 88 + if (rcmm->lut.enabled) { 89 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0); 90 + rcmm->lut.enabled = false; 91 + } 92 + 93 + return 0; 94 + } 95 + 96 + /* Enable LUT and program the new gamma table values. */ 97 + if (!rcmm->lut.enabled) { 98 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, CM2_LUT_CTRL_LUT_EN); 99 + rcmm->lut.enabled = true; 100 + } 101 + 102 + rcar_cmm_lut_write(rcmm, config->lut.table); 103 + 104 + return 0; 105 + } 106 + EXPORT_SYMBOL_GPL(rcar_cmm_setup); 107 + 108 + /* 109 + * rcar_cmm_enable() - Enable the CMM unit 110 + * @pdev: The platform device associated with the CMM instance 111 + * 112 + * When the output of the corresponding DU channel is routed to the CMM unit, 113 + * the unit shall be enabled before the DU channel is started, and remain 114 + * enabled until the channel is stopped. The CMM unit shall be disabled with 115 + * rcar_cmm_disable(). 116 + * 117 + * Calls to rcar_cmm_enable() and rcar_cmm_disable() are not reference-counted. 118 + * It is an error to attempt to enable an already enabled CMM unit, or to 119 + * attempt to disable a disabled unit. 120 + */ 121 + int rcar_cmm_enable(struct platform_device *pdev) 122 + { 123 + int ret; 124 + 125 + ret = pm_runtime_get_sync(&pdev->dev); 126 + if (ret < 0) 127 + return ret; 128 + 129 + return 0; 130 + } 131 + EXPORT_SYMBOL_GPL(rcar_cmm_enable); 132 + 133 + /* 134 + * rcar_cmm_disable() - Disable the CMM unit 135 + * @pdev: The platform device associated with the CMM instance 136 + * 137 + * See rcar_cmm_enable() for usage information. 138 + * 139 + * Disabling the CMM unit disable all the internal processing blocks. The CMM 140 + * state shall thus be restored with rcar_cmm_setup() when re-enabling the CMM 141 + * unit after the next rcar_cmm_enable() call. 142 + */ 143 + void rcar_cmm_disable(struct platform_device *pdev) 144 + { 145 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 146 + 147 + rcar_cmm_write(rcmm, CM2_LUT_CTRL, 0); 148 + rcmm->lut.enabled = false; 149 + 150 + pm_runtime_put(&pdev->dev); 151 + } 152 + EXPORT_SYMBOL_GPL(rcar_cmm_disable); 153 + 154 + /* 155 + * rcar_cmm_init() - Initialize the CMM unit 156 + * @pdev: The platform device associated with the CMM instance 157 + * 158 + * Return: 0 on success, -EPROBE_DEFER if the CMM is not available yet, 159 + * -ENODEV if the DRM_RCAR_CMM config option is disabled 160 + */ 161 + int rcar_cmm_init(struct platform_device *pdev) 162 + { 163 + struct rcar_cmm *rcmm = platform_get_drvdata(pdev); 164 + 165 + if (!rcmm) 166 + return -EPROBE_DEFER; 167 + 168 + return 0; 169 + } 170 + EXPORT_SYMBOL_GPL(rcar_cmm_init); 171 + 172 + static int rcar_cmm_probe(struct platform_device *pdev) 173 + { 174 + struct rcar_cmm *rcmm; 175 + 176 + rcmm = devm_kzalloc(&pdev->dev, sizeof(*rcmm), GFP_KERNEL); 177 + if (!rcmm) 178 + return -ENOMEM; 179 + platform_set_drvdata(pdev, rcmm); 180 + 181 + rcmm->base = devm_platform_ioremap_resource(pdev, 0); 182 + if (IS_ERR(rcmm->base)) 183 + return PTR_ERR(rcmm->base); 184 + 185 + pm_runtime_enable(&pdev->dev); 186 + 187 + return 0; 188 + } 189 + 190 + static int rcar_cmm_remove(struct platform_device *pdev) 191 + { 192 + pm_runtime_disable(&pdev->dev); 193 + 194 + return 0; 195 + } 196 + 197 + static const struct of_device_id rcar_cmm_of_table[] = { 198 + { .compatible = "renesas,rcar-gen3-cmm", }, 199 + { .compatible = "renesas,rcar-gen2-cmm", }, 200 + { }, 201 + }; 202 + MODULE_DEVICE_TABLE(of, rcar_cmm_of_table); 203 + 204 + static struct platform_driver rcar_cmm_platform_driver = { 205 + .probe = rcar_cmm_probe, 206 + .remove = rcar_cmm_remove, 207 + .driver = { 208 + .name = "rcar-cmm", 209 + .of_match_table = rcar_cmm_of_table, 210 + }, 211 + }; 212 + 213 + module_platform_driver(rcar_cmm_platform_driver); 214 + 215 + MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>"); 216 + MODULE_DESCRIPTION("Renesas R-Car CMM Driver"); 217 + MODULE_LICENSE("GPL v2");
+58
drivers/gpu/drm/rcar-du/rcar_cmm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * rcar_cmm.h -- R-Car Display Unit Color Management Module 4 + * 5 + * Copyright (C) 2019 Jacopo Mondi <jacopo+renesas@jmondi.org> 6 + */ 7 + 8 + #ifndef __RCAR_CMM_H__ 9 + #define __RCAR_CMM_H__ 10 + 11 + #define CM2_LUT_SIZE 256 12 + 13 + struct drm_color_lut; 14 + struct platform_device; 15 + 16 + /** 17 + * struct rcar_cmm_config - CMM configuration 18 + * 19 + * @lut: 1D-LUT configuration 20 + * @lut.table: 1D-LUT table entries. Disable LUT operations when NULL 21 + */ 22 + struct rcar_cmm_config { 23 + struct { 24 + struct drm_color_lut *table; 25 + } lut; 26 + }; 27 + 28 + #if IS_ENABLED(CONFIG_DRM_RCAR_CMM) 29 + int rcar_cmm_init(struct platform_device *pdev); 30 + 31 + int rcar_cmm_enable(struct platform_device *pdev); 32 + void rcar_cmm_disable(struct platform_device *pdev); 33 + 34 + int rcar_cmm_setup(struct platform_device *pdev, 35 + const struct rcar_cmm_config *config); 36 + #else 37 + static inline int rcar_cmm_init(struct platform_device *pdev) 38 + { 39 + return -ENODEV; 40 + } 41 + 42 + static inline int rcar_cmm_enable(struct platform_device *pdev) 43 + { 44 + return 0; 45 + } 46 + 47 + static inline void rcar_cmm_disable(struct platform_device *pdev) 48 + { 49 + } 50 + 51 + static inline int rcar_cmm_setup(struct platform_device *pdev, 52 + const struct rcar_cmm_config *config) 53 + { 54 + return 0; 55 + } 56 + #endif /* IS_ENABLED(CONFIG_DRM_RCAR_CMM) */ 57 + 58 + #endif /* __RCAR_CMM_H__ */
+71
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
··· 22 22 #include <drm/drm_plane_helper.h> 23 23 #include <drm/drm_vblank.h> 24 24 25 + #include "rcar_cmm.h" 25 26 #include "rcar_du_crtc.h" 26 27 #include "rcar_du_drv.h" 27 28 #include "rcar_du_encoder.h" ··· 477 476 } 478 477 479 478 /* ----------------------------------------------------------------------------- 479 + * Color Management Module (CMM) 480 + */ 481 + 482 + static int rcar_du_cmm_check(struct drm_crtc *crtc, 483 + struct drm_crtc_state *state) 484 + { 485 + struct drm_property_blob *drm_lut = state->gamma_lut; 486 + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); 487 + struct device *dev = rcrtc->dev->dev; 488 + 489 + if (!drm_lut) 490 + return 0; 491 + 492 + /* We only accept fully populated LUT tables. */ 493 + if (drm_color_lut_size(drm_lut) != CM2_LUT_SIZE) { 494 + dev_err(dev, "invalid gamma lut size: %zu bytes\n", 495 + drm_lut->length); 496 + return -EINVAL; 497 + } 498 + 499 + return 0; 500 + } 501 + 502 + static void rcar_du_cmm_setup(struct drm_crtc *crtc) 503 + { 504 + struct drm_property_blob *drm_lut = crtc->state->gamma_lut; 505 + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); 506 + struct rcar_cmm_config cmm_config = {}; 507 + 508 + if (!rcrtc->cmm) 509 + return; 510 + 511 + if (drm_lut) 512 + cmm_config.lut.table = (struct drm_color_lut *)drm_lut->data; 513 + 514 + rcar_cmm_setup(rcrtc->cmm, &cmm_config); 515 + } 516 + 517 + /* ----------------------------------------------------------------------------- 480 518 * Start/Stop and Suspend/Resume 481 519 */ 482 520 ··· 660 620 if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) 661 621 rcar_du_vsp_disable(rcrtc); 662 622 623 + if (rcrtc->cmm) 624 + rcar_cmm_disable(rcrtc->cmm); 625 + 663 626 /* 664 627 * Select switch sync mode. This stops display operation and configures 665 628 * the HSYNC and VSYNC signals as inputs. ··· 686 643 { 687 644 struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(state); 688 645 struct drm_encoder *encoder; 646 + int ret; 647 + 648 + ret = rcar_du_cmm_check(crtc, state); 649 + if (ret) 650 + return ret; 689 651 690 652 /* Store the routes from the CRTC output to the DU outputs. */ 691 653 rstate->outputs = 0; ··· 716 668 struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state); 717 669 struct rcar_du_device *rcdu = rcrtc->dev; 718 670 671 + if (rcrtc->cmm) 672 + rcar_cmm_enable(rcrtc->cmm); 719 673 rcar_du_crtc_get(rcrtc); 720 674 721 675 /* ··· 738 688 } 739 689 740 690 rcar_du_crtc_start(rcrtc); 691 + 692 + /* 693 + * TODO: The chip manual indicates that CMM tables should be written 694 + * after the DU channel has been activated. Investigate the impact 695 + * of this restriction on the first displayed frame. 696 + */ 697 + rcar_du_cmm_setup(crtc); 741 698 } 742 699 743 700 static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, ··· 799 742 * it in .atomic_flush() either. 800 743 */ 801 744 rcar_du_crtc_get(rcrtc); 745 + 746 + /* If the active state changed, we let .atomic_enable handle CMM. */ 747 + if (crtc->state->color_mgmt_changed && !crtc->state->active_changed) 748 + rcar_du_cmm_setup(crtc); 802 749 803 750 if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) 804 751 rcar_du_vsp_atomic_begin(rcrtc); ··· 1140 1079 .set_crc_source = rcar_du_crtc_set_crc_source, 1141 1080 .verify_crc_source = rcar_du_crtc_verify_crc_source, 1142 1081 .get_crc_sources = rcar_du_crtc_get_crc_sources, 1082 + .gamma_set = drm_atomic_helper_legacy_gamma_set, 1143 1083 }; 1144 1084 1145 1085 /* ----------------------------------------------------------------------------- ··· 1259 1197 NULL); 1260 1198 if (ret < 0) 1261 1199 return ret; 1200 + 1201 + /* CMM might be disabled for this CRTC. */ 1202 + if (rcdu->cmms[swindex]) { 1203 + rcrtc->cmm = rcdu->cmms[swindex]; 1204 + rgrp->cmms_mask |= BIT(hwindex % 2); 1205 + 1206 + drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE); 1207 + drm_crtc_enable_color_mgmt(crtc, 0, false, CM2_LUT_SIZE); 1208 + } 1262 1209 1263 1210 drm_crtc_helper_add(crtc, &crtc_helper_funcs); 1264 1211
+2
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
··· 39 39 * @vblank_wait: wait queue used to signal vertical blanking 40 40 * @vblank_count: number of vertical blanking interrupts to wait for 41 41 * @group: CRTC group this CRTC belongs to 42 + * @cmm: CMM associated with this CRTC 42 43 * @vsp: VSP feeding video to this CRTC 43 44 * @vsp_pipe: index of the VSP pipeline feeding video to this CRTC 44 45 * @writeback: the writeback connector ··· 65 64 unsigned int vblank_count; 66 65 67 66 struct rcar_du_group *group; 67 + struct platform_device *cmm; 68 68 struct rcar_du_vsp *vsp; 69 69 unsigned int vsp_pipe; 70 70
+5 -1
drivers/gpu/drm/rcar-du/rcar_du_drv.c
··· 399 399 | RCAR_DU_FEATURE_TVM_SYNC, 400 400 .channels_mask = BIT(0), 401 401 .routes = { 402 - /* R8A77970 has one RGB output and one LVDS output. */ 402 + /* 403 + * R8A77970 and R8A77980 have one RGB output and one LVDS 404 + * output. 405 + */ 403 406 [RCAR_DU_OUTPUT_DPAD0] = { 404 407 .possible_crtcs = BIT(0), 405 408 .port = 0, ··· 460 457 { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, 461 458 { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info }, 462 459 { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info }, 460 + { .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info }, 463 461 { .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info }, 464 462 { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info }, 465 463 { }
+2
drivers/gpu/drm/rcar-du/rcar_du_drv.h
··· 13 13 #include <linux/kernel.h> 14 14 #include <linux/wait.h> 15 15 16 + #include "rcar_cmm.h" 16 17 #include "rcar_du_crtc.h" 17 18 #include "rcar_du_group.h" 18 19 #include "rcar_du_vsp.h" ··· 86 85 struct rcar_du_encoder *encoders[RCAR_DU_OUTPUT_MAX]; 87 86 88 87 struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; 88 + struct platform_device *cmms[RCAR_DU_MAX_CRTCS]; 89 89 struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS]; 90 90 91 91 struct {
+10
drivers/gpu/drm/rcar-du/rcar_du_group.c
··· 135 135 static void rcar_du_group_setup(struct rcar_du_group *rgrp) 136 136 { 137 137 struct rcar_du_device *rcdu = rgrp->dev; 138 + u32 defr7 = DEFR7_CODE; 138 139 139 140 /* Enable extended features */ 140 141 rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE); ··· 147 146 rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); 148 147 149 148 rcar_du_group_setup_pins(rgrp); 149 + 150 + /* 151 + * TODO: Handle routing of the DU output to CMM dynamically, as we 152 + * should bypass CMM completely when no color management feature is 153 + * used. 154 + */ 155 + defr7 |= (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0) | 156 + (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0); 157 + rcar_du_group_write(rgrp, DEFR7, defr7); 150 158 151 159 if (rcdu->info->gen >= 2) { 152 160 rcar_du_group_setup_defr8(rgrp);
+2
drivers/gpu/drm/rcar-du/rcar_du_group.h
··· 22 22 * @mmio_offset: registers offset in the device memory map 23 23 * @index: group index 24 24 * @channels_mask: bitmask of populated DU channels in this group 25 + * @cmms_mask: bitmask of available CMMs in this group 25 26 * @num_crtcs: number of CRTCs in this group (1 or 2) 26 27 * @use_count: number of users of the group (rcar_du_group_(get|put)) 27 28 * @used_crtcs: number of CRTCs currently in use ··· 38 37 unsigned int index; 39 38 40 39 unsigned int channels_mask; 40 + unsigned int cmms_mask; 41 41 unsigned int num_crtcs; 42 42 unsigned int use_count; 43 43 unsigned int used_crtcs;
+88 -5
drivers/gpu/drm/rcar-du/rcar_du_kms.c
··· 17 17 #include <drm/drm_probe_helper.h> 18 18 #include <drm/drm_vblank.h> 19 19 20 + #include <linux/device.h> 20 21 #include <linux/of_graph.h> 22 + #include <linux/of_platform.h> 21 23 #include <linux/wait.h> 22 24 23 25 #include "rcar_du_crtc.h" ··· 544 542 static int rcar_du_vsps_init(struct rcar_du_device *rcdu) 545 543 { 546 544 const struct device_node *np = rcdu->dev->of_node; 545 + const char *vsps_prop_name = "renesas,vsps"; 547 546 struct of_phandle_args args; 548 547 struct { 549 548 struct device_node *np; ··· 560 557 * entry contains a pointer to the VSP DT node and a bitmask of the 561 558 * connected DU CRTCs. 562 559 */ 563 - cells = of_property_count_u32_elems(np, "vsps") / rcdu->num_crtcs - 1; 560 + ret = of_property_count_u32_elems(np, vsps_prop_name); 561 + if (ret < 0) { 562 + /* Backward compatibility with old DTBs. */ 563 + vsps_prop_name = "vsps"; 564 + ret = of_property_count_u32_elems(np, vsps_prop_name); 565 + } 566 + cells = ret / rcdu->num_crtcs - 1; 564 567 if (cells > 1) 565 568 return -EINVAL; 566 569 567 570 for (i = 0; i < rcdu->num_crtcs; ++i) { 568 571 unsigned int j; 569 572 570 - ret = of_parse_phandle_with_fixed_args(np, "vsps", cells, i, 571 - &args); 573 + ret = of_parse_phandle_with_fixed_args(np, vsps_prop_name, 574 + cells, i, &args); 572 575 if (ret < 0) 573 576 goto error; 574 577 ··· 596 587 597 588 /* 598 589 * Store the VSP pointer and pipe index in the CRTC. If the 599 - * second cell of the 'vsps' specifier isn't present, default 600 - * to 0 to remain compatible with older DT bindings. 590 + * second cell of the 'renesas,vsps' specifier isn't present, 591 + * default to 0 to remain compatible with older DT bindings. 601 592 */ 602 593 rcdu->crtcs[i].vsp = &rcdu->vsps[j]; 603 594 rcdu->crtcs[i].vsp_pipe = cells >= 1 ? args.args[0] : 0; ··· 625 616 of_node_put(vsps[i].np); 626 617 627 618 return ret; 619 + } 620 + 621 + static int rcar_du_cmm_init(struct rcar_du_device *rcdu) 622 + { 623 + const struct device_node *np = rcdu->dev->of_node; 624 + unsigned int i; 625 + int cells; 626 + 627 + cells = of_property_count_u32_elems(np, "renesas,cmms"); 628 + if (cells == -EINVAL) 629 + return 0; 630 + 631 + if (cells > rcdu->num_crtcs) { 632 + dev_err(rcdu->dev, 633 + "Invalid number of entries in 'renesas,cmms'\n"); 634 + return -EINVAL; 635 + } 636 + 637 + for (i = 0; i < cells; ++i) { 638 + struct platform_device *pdev; 639 + struct device_link *link; 640 + struct device_node *cmm; 641 + int ret; 642 + 643 + cmm = of_parse_phandle(np, "renesas,cmms", i); 644 + if (IS_ERR(cmm)) { 645 + dev_err(rcdu->dev, 646 + "Failed to parse 'renesas,cmms' property\n"); 647 + return PTR_ERR(cmm); 648 + } 649 + 650 + if (!of_device_is_available(cmm)) { 651 + /* It's fine to have a phandle to a non-enabled CMM. */ 652 + of_node_put(cmm); 653 + continue; 654 + } 655 + 656 + pdev = of_find_device_by_node(cmm); 657 + if (IS_ERR(pdev)) { 658 + dev_err(rcdu->dev, "No device found for CMM%u\n", i); 659 + of_node_put(cmm); 660 + return PTR_ERR(pdev); 661 + } 662 + 663 + of_node_put(cmm); 664 + 665 + /* 666 + * -ENODEV is used to report that the CMM config option is 667 + * disabled: return 0 and let the DU continue probing. 668 + */ 669 + ret = rcar_cmm_init(pdev); 670 + if (ret) 671 + return ret == -ENODEV ? 0 : ret; 672 + 673 + /* 674 + * Enforce suspend/resume ordering by making the CMM a provider 675 + * of the DU: CMM is suspended after and resumed before the DU. 676 + */ 677 + link = device_link_add(rcdu->dev, &pdev->dev, DL_FLAG_STATELESS); 678 + if (!link) { 679 + dev_err(rcdu->dev, 680 + "Failed to create device link to CMM%u\n", i); 681 + return -EINVAL; 682 + } 683 + 684 + rcdu->cmms[i] = pdev; 685 + } 686 + 687 + return 0; 628 688 } 629 689 630 690 int rcar_du_modeset_init(struct rcar_du_device *rcdu) ··· 785 707 if (ret < 0) 786 708 return ret; 787 709 } 710 + 711 + /* Initialize the Color Management Modules. */ 712 + ret = rcar_du_cmm_init(rcdu); 713 + if (ret) 714 + return ret; 788 715 789 716 /* Create the CRTCs. */ 790 717 for (swindex = 0, hwindex = 0; swindex < rcdu->num_crtcs; ++hwindex) {
+5
drivers/gpu/drm/rcar-du/rcar_du_regs.h
··· 197 197 #define DEFR6_MLOS1 (1 << 2) 198 198 #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE1) 199 199 200 + #define DEFR7 0x000ec 201 + #define DEFR7_CODE (0x7779 << 16) 202 + #define DEFR7_CMME1 BIT(6) 203 + #define DEFR7_CMME0 BIT(4) 204 + 200 205 /* ----------------------------------------------------------------------------- 201 206 * R8A7790-only Control Registers 202 207 */
+174 -144
drivers/gpu/drm/rcar-du/rcar_lvds.c
··· 21 21 #include <drm/drm_atomic.h> 22 22 #include <drm/drm_atomic_helper.h> 23 23 #include <drm/drm_bridge.h> 24 + #include <drm/drm_of.h> 24 25 #include <drm/drm_panel.h> 25 26 #include <drm/drm_probe_helper.h> 26 27 ··· 35 34 RCAR_LVDS_MODE_JEIDA = 0, 36 35 RCAR_LVDS_MODE_MIRROR = 1, 37 36 RCAR_LVDS_MODE_VESA = 4, 37 + }; 38 + 39 + enum rcar_lvds_link_type { 40 + RCAR_LVDS_SINGLE_LINK = 0, 41 + RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 1, 42 + RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 2, 38 43 }; 39 44 40 45 #define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ ··· 72 65 struct clk *dotclkin[2]; /* External DU clocks */ 73 66 } clocks; 74 67 75 - struct drm_display_mode display_mode; 76 - enum rcar_lvds_mode mode; 77 - 78 68 struct drm_bridge *companion; 79 - bool dual_link; 69 + enum rcar_lvds_link_type link_type; 80 70 }; 81 71 82 72 #define bridge_to_rcar_lvds(b) \ ··· 406 402 * Bridge 407 403 */ 408 404 409 - static void rcar_lvds_enable(struct drm_bridge *bridge) 405 + static enum rcar_lvds_mode rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds, 406 + const struct drm_connector *connector) 407 + { 408 + const struct drm_display_info *info; 409 + enum rcar_lvds_mode mode; 410 + 411 + /* 412 + * There is no API yet to retrieve LVDS mode from a bridge, only panels 413 + * are supported. 414 + */ 415 + if (!lvds->panel) 416 + return RCAR_LVDS_MODE_JEIDA; 417 + 418 + info = &connector->display_info; 419 + if (!info->num_bus_formats || !info->bus_formats) { 420 + dev_warn(lvds->dev, 421 + "no LVDS bus format reported, using JEIDA\n"); 422 + return RCAR_LVDS_MODE_JEIDA; 423 + } 424 + 425 + switch (info->bus_formats[0]) { 426 + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 427 + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 428 + mode = RCAR_LVDS_MODE_JEIDA; 429 + break; 430 + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 431 + mode = RCAR_LVDS_MODE_VESA; 432 + break; 433 + default: 434 + dev_warn(lvds->dev, 435 + "unsupported LVDS bus format 0x%04x, using JEIDA\n", 436 + info->bus_formats[0]); 437 + return RCAR_LVDS_MODE_JEIDA; 438 + } 439 + 440 + if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB) 441 + mode |= RCAR_LVDS_MODE_MIRROR; 442 + 443 + return mode; 444 + } 445 + 446 + static void __rcar_lvds_atomic_enable(struct drm_bridge *bridge, 447 + struct drm_atomic_state *state, 448 + struct drm_crtc *crtc, 449 + struct drm_connector *connector) 410 450 { 411 451 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); 412 - const struct drm_display_mode *mode = &lvds->display_mode; 413 452 u32 lvdhcr; 414 453 u32 lvdcr0; 415 454 int ret; ··· 462 415 return; 463 416 464 417 /* Enable the companion LVDS encoder in dual-link mode. */ 465 - if (lvds->dual_link && lvds->companion) 466 - lvds->companion->funcs->enable(lvds->companion); 418 + if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) 419 + __rcar_lvds_atomic_enable(lvds->companion, state, crtc, 420 + connector); 467 421 468 422 /* 469 423 * Hardcode the channels and control signals routing for now. ··· 488 440 rcar_lvds_write(lvds, LVDCHCR, lvdhcr); 489 441 490 442 if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) { 491 - /* 492 - * Configure vertical stripe based on the mode of operation of 493 - * the connected device. 494 - */ 495 - rcar_lvds_write(lvds, LVDSTRIPE, 496 - lvds->dual_link ? LVDSTRIPE_ST_ON : 0); 443 + u32 lvdstripe = 0; 444 + 445 + if (lvds->link_type != RCAR_LVDS_SINGLE_LINK) { 446 + /* 447 + * By default we generate even pixels from the primary 448 + * encoder and odd pixels from the companion encoder. 449 + * Swap pixels around if the sink requires odd pixels 450 + * from the primary encoder and even pixels from the 451 + * companion encoder. 452 + */ 453 + bool swap_pixels = lvds->link_type == 454 + RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; 455 + 456 + /* 457 + * Configure vertical stripe since we are dealing with 458 + * an LVDS dual-link connection. 459 + * 460 + * ST_SWAP is reserved for the companion encoder, only 461 + * set it in the primary encoder. 462 + */ 463 + lvdstripe = LVDSTRIPE_ST_ON 464 + | (lvds->companion && swap_pixels ? 465 + LVDSTRIPE_ST_SWAP : 0); 466 + } 467 + rcar_lvds_write(lvds, LVDSTRIPE, lvdstripe); 497 468 } 498 469 499 470 /* 500 471 * PLL clock configuration on all instances but the companion in 501 472 * dual-link mode. 502 473 */ 503 - if (!lvds->dual_link || lvds->companion) 474 + if (lvds->link_type == RCAR_LVDS_SINGLE_LINK || lvds->companion) { 475 + const struct drm_crtc_state *crtc_state = 476 + drm_atomic_get_new_crtc_state(state, crtc); 477 + const struct drm_display_mode *mode = 478 + &crtc_state->adjusted_mode; 479 + 504 480 lvds->info->pll_setup(lvds, mode->clock * 1000); 481 + } 505 482 506 483 /* Set the LVDS mode and select the input. */ 507 - lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; 484 + lvdcr0 = rcar_lvds_get_lvds_mode(lvds, connector) << LVDCR0_LVMD_SHIFT; 508 485 509 486 if (lvds->bridge.encoder) { 510 - /* 511 - * FIXME: We should really retrieve the CRTC through the state, 512 - * but how do we get a state pointer? 513 - */ 514 - if (drm_crtc_index(lvds->bridge.encoder->crtc) == 2) 487 + if (drm_crtc_index(crtc) == 2) 515 488 lvdcr0 |= LVDCR0_DUSEL; 516 489 } 517 490 ··· 589 520 } 590 521 } 591 522 592 - static void rcar_lvds_disable(struct drm_bridge *bridge) 523 + static void rcar_lvds_atomic_enable(struct drm_bridge *bridge, 524 + struct drm_atomic_state *state) 525 + { 526 + struct drm_connector *connector; 527 + struct drm_crtc *crtc; 528 + 529 + connector = drm_atomic_get_new_connector_for_encoder(state, 530 + bridge->encoder); 531 + crtc = drm_atomic_get_new_connector_state(state, connector)->crtc; 532 + 533 + __rcar_lvds_atomic_enable(bridge, state, crtc, connector); 534 + } 535 + 536 + static void rcar_lvds_atomic_disable(struct drm_bridge *bridge, 537 + struct drm_atomic_state *state) 593 538 { 594 539 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); 595 540 ··· 617 534 rcar_lvds_write(lvds, LVDPLLCR, 0); 618 535 619 536 /* Disable the companion LVDS encoder in dual-link mode. */ 620 - if (lvds->dual_link && lvds->companion) 621 - lvds->companion->funcs->disable(lvds->companion); 537 + if (lvds->link_type != RCAR_LVDS_SINGLE_LINK && lvds->companion) 538 + lvds->companion->funcs->atomic_disable(lvds->companion, state); 622 539 623 540 clk_disable_unprepare(lvds->clocks.mod); 624 541 } ··· 639 556 adjusted_mode->clock = clamp(adjusted_mode->clock, min_freq, 148500); 640 557 641 558 return true; 642 - } 643 - 644 - static void rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds) 645 - { 646 - struct drm_display_info *info = &lvds->connector.display_info; 647 - enum rcar_lvds_mode mode; 648 - 649 - /* 650 - * There is no API yet to retrieve LVDS mode from a bridge, only panels 651 - * are supported. 652 - */ 653 - if (!lvds->panel) 654 - return; 655 - 656 - if (!info->num_bus_formats || !info->bus_formats) { 657 - dev_err(lvds->dev, "no LVDS bus format reported\n"); 658 - return; 659 - } 660 - 661 - switch (info->bus_formats[0]) { 662 - case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 663 - case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 664 - mode = RCAR_LVDS_MODE_JEIDA; 665 - break; 666 - case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 667 - mode = RCAR_LVDS_MODE_VESA; 668 - break; 669 - default: 670 - dev_err(lvds->dev, "unsupported LVDS bus format 0x%04x\n", 671 - info->bus_formats[0]); 672 - return; 673 - } 674 - 675 - if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB) 676 - mode |= RCAR_LVDS_MODE_MIRROR; 677 - 678 - lvds->mode = mode; 679 - } 680 - 681 - static void rcar_lvds_mode_set(struct drm_bridge *bridge, 682 - const struct drm_display_mode *mode, 683 - const struct drm_display_mode *adjusted_mode) 684 - { 685 - struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); 686 - 687 - lvds->display_mode = *adjusted_mode; 688 - 689 - rcar_lvds_get_lvds_mode(lvds); 690 559 } 691 560 692 561 static int rcar_lvds_attach(struct drm_bridge *bridge) ··· 682 647 static const struct drm_bridge_funcs rcar_lvds_bridge_ops = { 683 648 .attach = rcar_lvds_attach, 684 649 .detach = rcar_lvds_detach, 685 - .enable = rcar_lvds_enable, 686 - .disable = rcar_lvds_disable, 650 + .atomic_enable = rcar_lvds_atomic_enable, 651 + .atomic_disable = rcar_lvds_atomic_disable, 687 652 .mode_fixup = rcar_lvds_mode_fixup, 688 - .mode_set = rcar_lvds_mode_set, 689 653 }; 690 654 691 655 bool rcar_lvds_dual_link(struct drm_bridge *bridge) 692 656 { 693 657 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); 694 658 695 - return lvds->dual_link; 659 + return lvds->link_type != RCAR_LVDS_SINGLE_LINK; 696 660 } 697 661 EXPORT_SYMBOL_GPL(rcar_lvds_dual_link); 698 662 ··· 703 669 { 704 670 const struct of_device_id *match; 705 671 struct device_node *companion; 672 + struct device_node *port0, *port1; 673 + struct rcar_lvds *companion_lvds; 706 674 struct device *dev = lvds->dev; 675 + int dual_link; 707 676 int ret = 0; 708 677 709 678 /* Locate the companion LVDS encoder for dual-link operation, if any. */ ··· 725 688 goto done; 726 689 } 727 690 691 + /* 692 + * We need to work out if the sink is expecting us to function in 693 + * dual-link mode. We do this by looking at the DT port nodes we are 694 + * connected to, if they are marked as expecting even pixels and 695 + * odd pixels than we need to enable vertical stripe output. 696 + */ 697 + port0 = of_graph_get_port_by_id(dev->of_node, 1); 698 + port1 = of_graph_get_port_by_id(companion, 1); 699 + dual_link = drm_of_lvds_get_dual_link_pixel_order(port0, port1); 700 + of_node_put(port0); 701 + of_node_put(port1); 702 + 703 + switch (dual_link) { 704 + case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: 705 + lvds->link_type = RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; 706 + break; 707 + case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: 708 + lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; 709 + break; 710 + default: 711 + /* 712 + * Early dual-link bridge specific implementations populate the 713 + * timings field of drm_bridge. If the flag is set, we assume 714 + * that we are expected to generate even pixels from the primary 715 + * encoder, and odd pixels from the companion encoder. 716 + */ 717 + if (lvds->next_bridge && lvds->next_bridge->timings && 718 + lvds->next_bridge->timings->dual_link) 719 + lvds->link_type = RCAR_LVDS_DUAL_LINK_EVEN_ODD_PIXELS; 720 + else 721 + lvds->link_type = RCAR_LVDS_SINGLE_LINK; 722 + } 723 + 724 + if (lvds->link_type == RCAR_LVDS_SINGLE_LINK) { 725 + dev_dbg(dev, "Single-link configuration detected\n"); 726 + goto done; 727 + } 728 + 728 729 lvds->companion = of_drm_find_bridge(companion); 729 730 if (!lvds->companion) { 730 731 ret = -EPROBE_DEFER; 731 732 goto done; 732 733 } 733 734 734 - dev_dbg(dev, "Found companion encoder %pOF\n", companion); 735 + dev_dbg(dev, 736 + "Dual-link configuration detected (companion encoder %pOF)\n", 737 + companion); 738 + 739 + if (lvds->link_type == RCAR_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) 740 + dev_dbg(dev, "Data swapping required\n"); 741 + 742 + /* 743 + * FIXME: We should not be messing with the companion encoder private 744 + * data from the primary encoder, we should rather let the companion 745 + * encoder work things out on its own. However, the companion encoder 746 + * doesn't hold a reference to the primary encoder, and 747 + * drm_of_lvds_get_dual_link_pixel_order needs to be given references 748 + * to the output ports of both encoders, therefore leave it like this 749 + * for the time being. 750 + */ 751 + companion_lvds = bridge_to_rcar_lvds(lvds->companion); 752 + companion_lvds->link_type = lvds->link_type; 735 753 736 754 done: 737 755 of_node_put(companion); ··· 796 704 797 705 static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) 798 706 { 799 - struct device_node *local_output = NULL; 800 - struct device_node *remote_input = NULL; 801 - struct device_node *remote = NULL; 802 - struct device_node *node; 803 - bool is_bridge = false; 804 - int ret = 0; 707 + int ret; 805 708 806 - local_output = of_graph_get_endpoint_by_regs(lvds->dev->of_node, 1, 0); 807 - if (!local_output) { 808 - dev_dbg(lvds->dev, "unconnected port@1\n"); 809 - ret = -ENODEV; 709 + ret = drm_of_find_panel_or_bridge(lvds->dev->of_node, 1, 0, 710 + &lvds->panel, &lvds->next_bridge); 711 + if (ret) 810 712 goto done; 811 - } 812 713 813 - /* 814 - * Locate the connected entity and infer its type from the number of 815 - * endpoints. 816 - */ 817 - remote = of_graph_get_remote_port_parent(local_output); 818 - if (!remote) { 819 - dev_dbg(lvds->dev, "unconnected endpoint %pOF\n", local_output); 820 - ret = -ENODEV; 821 - goto done; 822 - } 823 - 824 - if (!of_device_is_available(remote)) { 825 - dev_dbg(lvds->dev, "connected entity %pOF is disabled\n", 826 - remote); 827 - ret = -ENODEV; 828 - goto done; 829 - } 830 - 831 - remote_input = of_graph_get_remote_endpoint(local_output); 832 - 833 - for_each_endpoint_of_node(remote, node) { 834 - if (node != remote_input) { 835 - /* 836 - * We've found one endpoint other than the input, this 837 - * must be a bridge. 838 - */ 839 - is_bridge = true; 840 - of_node_put(node); 841 - break; 842 - } 843 - } 844 - 845 - if (is_bridge) { 846 - lvds->next_bridge = of_drm_find_bridge(remote); 847 - if (!lvds->next_bridge) { 848 - ret = -EPROBE_DEFER; 849 - goto done; 850 - } 851 - 852 - if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) 853 - lvds->dual_link = lvds->next_bridge->timings 854 - ? lvds->next_bridge->timings->dual_link 855 - : false; 856 - } else { 857 - lvds->panel = of_drm_find_panel(remote); 858 - if (IS_ERR(lvds->panel)) { 859 - ret = PTR_ERR(lvds->panel); 860 - goto done; 861 - } 862 - } 863 - 864 - if (lvds->dual_link) 714 + if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) 865 715 ret = rcar_lvds_parse_dt_companion(lvds); 866 716 867 717 done: 868 - of_node_put(local_output); 869 - of_node_put(remote_input); 870 - of_node_put(remote); 871 - 872 718 /* 873 719 * On D3/E3 the LVDS encoder provides a clock to the DU, which can be 874 720 * used for the DPAD output even when the LVDS output is not connected.
+20
include/drm/drm_of.h
··· 16 16 struct drm_bridge; 17 17 struct device_node; 18 18 19 + /** 20 + * enum drm_lvds_dual_link_pixels - Pixel order of an LVDS dual-link connection 21 + * @DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: Even pixels are expected to be generated 22 + * from the first port, odd pixels from the second port 23 + * @DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: Odd pixels are expected to be generated 24 + * from the first port, even pixels from the second port 25 + */ 26 + enum drm_lvds_dual_link_pixels { 27 + DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 0, 28 + DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 1, 29 + }; 30 + 19 31 #ifdef CONFIG_OF 20 32 uint32_t drm_of_crtc_port_mask(struct drm_device *dev, 21 33 struct device_node *port); ··· 47 35 int port, int endpoint, 48 36 struct drm_panel **panel, 49 37 struct drm_bridge **bridge); 38 + int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, 39 + const struct device_node *port2); 50 40 #else 51 41 static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev, 52 42 struct device_node *port) ··· 88 74 int port, int endpoint, 89 75 struct drm_panel **panel, 90 76 struct drm_bridge **bridge) 77 + { 78 + return -EINVAL; 79 + } 80 + 81 + int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, 82 + const struct device_node *port2) 91 83 { 92 84 return -EINVAL; 93 85 }