| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * rcar_du_group.c -- R-Car Display Unit Channels Pair |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2013-2015 Renesas Electronics Corporation |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | 8 | */ |
|---|
| 13 | 9 | |
|---|
| 14 | 10 | /* |
|---|
| .. | .. |
|---|
| 60 | 56 | static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) |
|---|
| 61 | 57 | { |
|---|
| 62 | 58 | struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 63 | | - unsigned int possible_crtcs = |
|---|
| 64 | | - rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs; |
|---|
| 65 | 59 | u32 defr8 = DEFR8_CODE; |
|---|
| 66 | 60 | |
|---|
| 67 | 61 | if (rcdu->info->gen < 3) { |
|---|
| .. | .. |
|---|
| 73 | 67 | * DU instances that support it. |
|---|
| 74 | 68 | */ |
|---|
| 75 | 69 | if (rgrp->index == 0) { |
|---|
| 76 | | - if (possible_crtcs > 1) |
|---|
| 77 | | - defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); |
|---|
| 70 | + defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); |
|---|
| 78 | 71 | if (rgrp->dev->vspd1_sink == 2) |
|---|
| 79 | 72 | defr8 |= DEFR8_VSCS; |
|---|
| 80 | 73 | } |
|---|
| 81 | 74 | } else { |
|---|
| 82 | 75 | /* |
|---|
| 83 | | - * On Gen3 VSPD routing can't be configured, but DPAD routing |
|---|
| 84 | | - * needs to be set despite having a single option available. |
|---|
| 76 | + * On Gen3 VSPD routing can't be configured, and DPAD routing |
|---|
| 77 | + * is set in the group corresponding to the DPAD output (no Gen3 |
|---|
| 78 | + * SoC has multiple DPAD sources belonging to separate groups). |
|---|
| 85 | 79 | */ |
|---|
| 86 | | - unsigned int rgb_crtc = ffs(possible_crtcs) - 1; |
|---|
| 87 | | - struct rcar_du_crtc *crtc = &rcdu->crtcs[rgb_crtc]; |
|---|
| 88 | | - |
|---|
| 89 | | - if (crtc->index / 2 == rgrp->index) |
|---|
| 90 | | - defr8 |= DEFR8_DRGBS_DU(crtc->index); |
|---|
| 80 | + if (rgrp->index == rcdu->dpad0_source / 2) |
|---|
| 81 | + defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); |
|---|
| 91 | 82 | } |
|---|
| 92 | 83 | |
|---|
| 93 | 84 | rcar_du_group_write(rgrp, DEFR8, defr8); |
|---|
| 94 | 85 | } |
|---|
| 95 | 86 | |
|---|
| 87 | +static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp) |
|---|
| 88 | +{ |
|---|
| 89 | + struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 90 | + struct rcar_du_crtc *rcrtc; |
|---|
| 91 | + unsigned int num_crtcs = 0; |
|---|
| 92 | + unsigned int i; |
|---|
| 93 | + u32 didsr; |
|---|
| 94 | + |
|---|
| 95 | + /* |
|---|
| 96 | + * Configure input dot clock routing with a hardcoded configuration. If |
|---|
| 97 | + * the DU channel can use the LVDS encoder output clock as the dot |
|---|
| 98 | + * clock, do so. Otherwise route DU_DOTCLKINn signal to DUn. |
|---|
| 99 | + * |
|---|
| 100 | + * Each channel can then select between the dot clock configured here |
|---|
| 101 | + * and the clock provided by the CPG through the ESCR register. |
|---|
| 102 | + */ |
|---|
| 103 | + if (rcdu->info->gen < 3 && rgrp->index == 0) { |
|---|
| 104 | + /* |
|---|
| 105 | + * On Gen2 a single register in the first group controls dot |
|---|
| 106 | + * clock selection for all channels. |
|---|
| 107 | + */ |
|---|
| 108 | + rcrtc = rcdu->crtcs; |
|---|
| 109 | + num_crtcs = rcdu->num_crtcs; |
|---|
| 110 | + } else if (rcdu->info->gen == 3 && rgrp->num_crtcs > 1) { |
|---|
| 111 | + /* |
|---|
| 112 | + * On Gen3 dot clocks are setup through per-group registers, |
|---|
| 113 | + * only available when the group has two channels. |
|---|
| 114 | + */ |
|---|
| 115 | + rcrtc = &rcdu->crtcs[rgrp->index * 2]; |
|---|
| 116 | + num_crtcs = rgrp->num_crtcs; |
|---|
| 117 | + } |
|---|
| 118 | + |
|---|
| 119 | + if (!num_crtcs) |
|---|
| 120 | + return; |
|---|
| 121 | + |
|---|
| 122 | + didsr = DIDSR_CODE; |
|---|
| 123 | + for (i = 0; i < num_crtcs; ++i, ++rcrtc) { |
|---|
| 124 | + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) |
|---|
| 125 | + didsr |= DIDSR_LCDS_LVDS0(i) |
|---|
| 126 | + | DIDSR_PDCS_CLK(i, 0); |
|---|
| 127 | + else |
|---|
| 128 | + didsr |= DIDSR_LCDS_DCLKIN(i) |
|---|
| 129 | + | DIDSR_PDCS_CLK(i, 0); |
|---|
| 130 | + } |
|---|
| 131 | + |
|---|
| 132 | + rcar_du_group_write(rgrp, DIDSR, didsr); |
|---|
| 133 | +} |
|---|
| 134 | + |
|---|
| 96 | 135 | static void rcar_du_group_setup(struct rcar_du_group *rgrp) |
|---|
| 97 | 136 | { |
|---|
| 98 | 137 | struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 138 | + u32 defr7 = DEFR7_CODE; |
|---|
| 99 | 139 | |
|---|
| 100 | 140 | /* Enable extended features */ |
|---|
| 101 | 141 | rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE); |
|---|
| .. | .. |
|---|
| 108 | 148 | |
|---|
| 109 | 149 | rcar_du_group_setup_pins(rgrp); |
|---|
| 110 | 150 | |
|---|
| 111 | | - if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { |
|---|
| 112 | | - rcar_du_group_setup_defr8(rgrp); |
|---|
| 151 | + /* |
|---|
| 152 | + * TODO: Handle routing of the DU output to CMM dynamically, as we |
|---|
| 153 | + * should bypass CMM completely when no color management feature is |
|---|
| 154 | + * used. |
|---|
| 155 | + */ |
|---|
| 156 | + defr7 |= (rgrp->cmms_mask & BIT(1) ? DEFR7_CMME1 : 0) | |
|---|
| 157 | + (rgrp->cmms_mask & BIT(0) ? DEFR7_CMME0 : 0); |
|---|
| 158 | + rcar_du_group_write(rgrp, DEFR7, defr7); |
|---|
| 113 | 159 | |
|---|
| 114 | | - /* |
|---|
| 115 | | - * Configure input dot clock routing. We currently hardcode the |
|---|
| 116 | | - * configuration to routing DOTCLKINn to DUn. Register fields |
|---|
| 117 | | - * depend on the DU generation, but the resulting value is 0 in |
|---|
| 118 | | - * all cases. |
|---|
| 119 | | - * |
|---|
| 120 | | - * On Gen2 a single register in the first group controls dot |
|---|
| 121 | | - * clock selection for all channels, while on Gen3 dot clocks |
|---|
| 122 | | - * are setup through per-group registers, only available when |
|---|
| 123 | | - * the group has two channels. |
|---|
| 124 | | - */ |
|---|
| 125 | | - if ((rcdu->info->gen < 3 && rgrp->index == 0) || |
|---|
| 126 | | - (rcdu->info->gen == 3 && rgrp->num_crtcs > 1)) |
|---|
| 127 | | - rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE); |
|---|
| 160 | + if (rcdu->info->gen >= 2) { |
|---|
| 161 | + rcar_du_group_setup_defr8(rgrp); |
|---|
| 162 | + rcar_du_group_setup_didsr(rgrp); |
|---|
| 128 | 163 | } |
|---|
| 129 | 164 | |
|---|
| 130 | 165 | if (rcdu->info->gen >= 3) |
|---|
| .. | .. |
|---|
| 177 | 212 | |
|---|
| 178 | 213 | static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) |
|---|
| 179 | 214 | { |
|---|
| 180 | | - rcar_du_group_write(rgrp, DSYSR, |
|---|
| 181 | | - (rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | |
|---|
| 182 | | - (start ? DSYSR_DEN : DSYSR_DRES)); |
|---|
| 215 | + struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 216 | + |
|---|
| 217 | + /* |
|---|
| 218 | + * Group start/stop is controlled by the DRES and DEN bits of DSYSR0 |
|---|
| 219 | + * for the first group and DSYSR2 for the second group. On most DU |
|---|
| 220 | + * instances, this maps to the first CRTC of the group, and we can just |
|---|
| 221 | + * use rcar_du_crtc_dsysr_clr_set() to access the correct DSYSR. On |
|---|
| 222 | + * M3-N, however, DU2 doesn't exist, but DSYSR2 does. We thus need to |
|---|
| 223 | + * access the register directly using group read/write. |
|---|
| 224 | + */ |
|---|
| 225 | + if (rcdu->info->channels_mask & BIT(rgrp->index * 2)) { |
|---|
| 226 | + struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2]; |
|---|
| 227 | + |
|---|
| 228 | + rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN, |
|---|
| 229 | + start ? DSYSR_DEN : DSYSR_DRES); |
|---|
| 230 | + } else { |
|---|
| 231 | + rcar_du_group_write(rgrp, DSYSR, |
|---|
| 232 | + start ? DSYSR_DEN : DSYSR_DRES); |
|---|
| 233 | + } |
|---|
| 183 | 234 | } |
|---|
| 184 | 235 | |
|---|
| 185 | 236 | void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) |
|---|
| .. | .. |
|---|
| 221 | 272 | unsigned int index; |
|---|
| 222 | 273 | int ret; |
|---|
| 223 | 274 | |
|---|
| 224 | | - if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_EXT_CTRL_REGS)) |
|---|
| 275 | + if (rcdu->info->gen < 2) |
|---|
| 225 | 276 | return 0; |
|---|
| 226 | 277 | |
|---|
| 227 | 278 | /* |
|---|
| .. | .. |
|---|
| 246 | 297 | return 0; |
|---|
| 247 | 298 | } |
|---|
| 248 | 299 | |
|---|
| 300 | +static void rcar_du_group_set_dpad_levels(struct rcar_du_group *rgrp) |
|---|
| 301 | +{ |
|---|
| 302 | + static const u32 doflr_values[2] = { |
|---|
| 303 | + DOFLR_HSYCFL0 | DOFLR_VSYCFL0 | DOFLR_ODDFL0 | |
|---|
| 304 | + DOFLR_DISPFL0 | DOFLR_CDEFL0 | DOFLR_RGBFL0, |
|---|
| 305 | + DOFLR_HSYCFL1 | DOFLR_VSYCFL1 | DOFLR_ODDFL1 | |
|---|
| 306 | + DOFLR_DISPFL1 | DOFLR_CDEFL1 | DOFLR_RGBFL1, |
|---|
| 307 | + }; |
|---|
| 308 | + static const u32 dpad_mask = BIT(RCAR_DU_OUTPUT_DPAD1) |
|---|
| 309 | + | BIT(RCAR_DU_OUTPUT_DPAD0); |
|---|
| 310 | + struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 311 | + u32 doflr = DOFLR_CODE; |
|---|
| 312 | + unsigned int i; |
|---|
| 313 | + |
|---|
| 314 | + if (rcdu->info->gen < 2) |
|---|
| 315 | + return; |
|---|
| 316 | + |
|---|
| 317 | + /* |
|---|
| 318 | + * The DPAD outputs can't be controlled directly. However, the parallel |
|---|
| 319 | + * output of the DU channels routed to DPAD can be set to fixed levels |
|---|
| 320 | + * through the DOFLR group register. Use this to turn the DPAD on or off |
|---|
| 321 | + * by driving fixed low-level signals at the output of any DU channel |
|---|
| 322 | + * not routed to a DPAD output. This doesn't affect the DU output |
|---|
| 323 | + * signals going to other outputs, such as the internal LVDS and HDMI |
|---|
| 324 | + * encoders. |
|---|
| 325 | + */ |
|---|
| 326 | + |
|---|
| 327 | + for (i = 0; i < rgrp->num_crtcs; ++i) { |
|---|
| 328 | + struct rcar_du_crtc_state *rstate; |
|---|
| 329 | + struct rcar_du_crtc *rcrtc; |
|---|
| 330 | + |
|---|
| 331 | + rcrtc = &rcdu->crtcs[rgrp->index * 2 + i]; |
|---|
| 332 | + rstate = to_rcar_crtc_state(rcrtc->crtc.state); |
|---|
| 333 | + |
|---|
| 334 | + if (!(rstate->outputs & dpad_mask)) |
|---|
| 335 | + doflr |= doflr_values[i]; |
|---|
| 336 | + } |
|---|
| 337 | + |
|---|
| 338 | + rcar_du_group_write(rgrp, DOFLR, doflr); |
|---|
| 339 | +} |
|---|
| 340 | + |
|---|
| 249 | 341 | int rcar_du_group_set_routing(struct rcar_du_group *rgrp) |
|---|
| 250 | 342 | { |
|---|
| 251 | | - struct rcar_du_crtc *crtc0 = &rgrp->dev->crtcs[rgrp->index * 2]; |
|---|
| 343 | + struct rcar_du_device *rcdu = rgrp->dev; |
|---|
| 252 | 344 | u32 dorcr = rcar_du_group_read(rgrp, DORCR); |
|---|
| 253 | 345 | |
|---|
| 254 | 346 | dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); |
|---|
| .. | .. |
|---|
| 258 | 350 | * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1 |
|---|
| 259 | 351 | * by default. |
|---|
| 260 | 352 | */ |
|---|
| 261 | | - if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) |
|---|
| 353 | + if (rcdu->dpad1_source == rgrp->index * 2) |
|---|
| 262 | 354 | dorcr |= DORCR_PG2D_DS1; |
|---|
| 263 | 355 | else |
|---|
| 264 | 356 | dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; |
|---|
| 265 | 357 | |
|---|
| 266 | 358 | rcar_du_group_write(rgrp, DORCR, dorcr); |
|---|
| 267 | 359 | |
|---|
| 360 | + rcar_du_group_set_dpad_levels(rgrp); |
|---|
| 361 | + |
|---|
| 268 | 362 | return rcar_du_set_dpad0_vsp1_routing(rgrp->dev); |
|---|
| 269 | 363 | } |
|---|