diff options
Diffstat (limited to 'drivers/gpu')
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds.c | 359 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 |
2 files changed, 355 insertions, 47 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index ce0eb68c3416..173d7ad0b991 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c | |||
| @@ -24,6 +24,8 @@ | |||
| 24 | 24 | ||
| 25 | #include "rcar_lvds_regs.h" | 25 | #include "rcar_lvds_regs.h" |
| 26 | 26 | ||
| 27 | struct rcar_lvds; | ||
| 28 | |||
| 27 | /* Keep in sync with the LVDCR0.LVMD hardware register values. */ | 29 | /* Keep in sync with the LVDCR0.LVMD hardware register values. */ |
| 28 | enum rcar_lvds_mode { | 30 | enum rcar_lvds_mode { |
| 29 | RCAR_LVDS_MODE_JEIDA = 0, | 31 | RCAR_LVDS_MODE_JEIDA = 0, |
| @@ -31,14 +33,16 @@ enum rcar_lvds_mode { | |||
| 31 | RCAR_LVDS_MODE_VESA = 4, | 33 | RCAR_LVDS_MODE_VESA = 4, |
| 32 | }; | 34 | }; |
| 33 | 35 | ||
| 34 | #define RCAR_LVDS_QUIRK_LANES (1 << 0) /* LVDS lanes 1 and 3 inverted */ | 36 | #define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ |
| 35 | #define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */ | 37 | #define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ |
| 36 | #define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ | 38 | #define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of Gen3 but E3) */ |
| 37 | /* on R8A77970/R8A7799x */ | 39 | #define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ |
| 40 | #define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */ | ||
| 38 | 41 | ||
| 39 | struct rcar_lvds_device_info { | 42 | struct rcar_lvds_device_info { |
| 40 | unsigned int gen; | 43 | unsigned int gen; |
| 41 | unsigned int quirks; | 44 | unsigned int quirks; |
| 45 | void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); | ||
| 42 | }; | 46 | }; |
| 43 | 47 | ||
| 44 | struct rcar_lvds { | 48 | struct rcar_lvds { |
| @@ -52,7 +56,11 @@ struct rcar_lvds { | |||
| 52 | struct drm_panel *panel; | 56 | struct drm_panel *panel; |
| 53 | 57 | ||
| 54 | void __iomem *mmio; | 58 | void __iomem *mmio; |
| 55 | struct clk *clock; | 59 | struct { |
| 60 | struct clk *mod; /* CPG module clock */ | ||
| 61 | struct clk *extal; /* External clock */ | ||
| 62 | struct clk *dotclkin[2]; /* External DU clocks */ | ||
| 63 | } clocks; | ||
| 56 | bool enabled; | 64 | bool enabled; |
| 57 | 65 | ||
| 58 | struct drm_display_mode display_mode; | 66 | struct drm_display_mode display_mode; |
| @@ -128,33 +136,216 @@ static const struct drm_connector_funcs rcar_lvds_conn_funcs = { | |||
| 128 | }; | 136 | }; |
| 129 | 137 | ||
| 130 | /* ----------------------------------------------------------------------------- | 138 | /* ----------------------------------------------------------------------------- |
| 131 | * Bridge | 139 | * PLL Setup |
| 132 | */ | 140 | */ |
| 133 | 141 | ||
| 134 | static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) | 142 | static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq) |
| 135 | { | 143 | { |
| 136 | if (freq < 39000) | 144 | u32 val; |
| 137 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; | 145 | |
| 138 | else if (freq < 61000) | 146 | if (freq < 39000000) |
| 139 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; | 147 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; |
| 140 | else if (freq < 121000) | 148 | else if (freq < 61000000) |
| 141 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | 149 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; |
| 150 | else if (freq < 121000000) | ||
| 151 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | ||
| 142 | else | 152 | else |
| 143 | return LVDPLLCR_PLLDLYCNT_150M; | 153 | val = LVDPLLCR_PLLDLYCNT_150M; |
| 154 | |||
| 155 | rcar_lvds_write(lvds, LVDPLLCR, val); | ||
| 144 | } | 156 | } |
| 145 | 157 | ||
| 146 | static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) | 158 | static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq) |
| 147 | { | 159 | { |
| 148 | if (freq < 42000) | 160 | u32 val; |
| 149 | return LVDPLLCR_PLLDIVCNT_42M; | 161 | |
| 150 | else if (freq < 85000) | 162 | if (freq < 42000000) |
| 151 | return LVDPLLCR_PLLDIVCNT_85M; | 163 | val = LVDPLLCR_PLLDIVCNT_42M; |
| 152 | else if (freq < 128000) | 164 | else if (freq < 85000000) |
| 153 | return LVDPLLCR_PLLDIVCNT_128M; | 165 | val = LVDPLLCR_PLLDIVCNT_85M; |
| 166 | else if (freq < 128000000) | ||
| 167 | val = LVDPLLCR_PLLDIVCNT_128M; | ||
| 154 | else | 168 | else |
| 155 | return LVDPLLCR_PLLDIVCNT_148M; | 169 | val = LVDPLLCR_PLLDIVCNT_148M; |
| 170 | |||
| 171 | rcar_lvds_write(lvds, LVDPLLCR, val); | ||
| 156 | } | 172 | } |
| 157 | 173 | ||
| 174 | struct pll_info { | ||
| 175 | unsigned long diff; | ||
| 176 | unsigned int pll_m; | ||
| 177 | unsigned int pll_n; | ||
| 178 | unsigned int pll_e; | ||
| 179 | unsigned int div; | ||
| 180 | u32 clksel; | ||
| 181 | }; | ||
| 182 | |||
| 183 | static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, | ||
| 184 | unsigned long target, struct pll_info *pll, | ||
| 185 | u32 clksel) | ||
| 186 | { | ||
| 187 | unsigned long output; | ||
| 188 | unsigned long fin; | ||
| 189 | unsigned int m_min; | ||
| 190 | unsigned int m_max; | ||
| 191 | unsigned int m; | ||
| 192 | int error; | ||
| 193 | |||
| 194 | if (!clk) | ||
| 195 | return; | ||
| 196 | |||
| 197 | /* | ||
| 198 | * The LVDS PLL is made of a pre-divider and a multiplier (strangely | ||
| 199 | * enough called M and N respectively), followed by a post-divider E. | ||
| 200 | * | ||
| 201 | * ,-----. ,-----. ,-----. ,-----. | ||
| 202 | * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout | ||
| 203 | * `-----' ,-> | | `-----' | `-----' | ||
| 204 | * | `-----' | | ||
| 205 | * | ,-----. | | ||
| 206 | * `-------- | 1/N | <-------' | ||
| 207 | * `-----' | ||
| 208 | * | ||
| 209 | * The clock output by the PLL is then further divided by a programmable | ||
| 210 | * divider DIV to achieve the desired target frequency. Finally, an | ||
| 211 | * optional fixed /7 divider is used to convert the bit clock to a pixel | ||
| 212 | * clock (as LVDS transmits 7 bits per lane per clock sample). | ||
| 213 | * | ||
| 214 | * ,-------. ,-----. |\ | ||
| 215 | * Fout --> | 1/DIV | --> | 1/7 | --> | | | ||
| 216 | * `-------' | `-----' | | --> dot clock | ||
| 217 | * `------------> | | | ||
| 218 | * |/ | ||
| 219 | * | ||
| 220 | * The /7 divider is optional when the LVDS PLL is used to generate a | ||
| 221 | * dot clock for the DU RGB output, without using the LVDS encoder. We | ||
| 222 | * don't support this configuration yet. | ||
| 223 | * | ||
| 224 | * The PLL allowed input frequency range is 12 MHz to 192 MHz. | ||
| 225 | */ | ||
| 226 | |||
| 227 | fin = clk_get_rate(clk); | ||
| 228 | if (fin < 12000000 || fin > 192000000) | ||
| 229 | return; | ||
| 230 | |||
| 231 | /* | ||
| 232 | * The comparison frequency range is 12 MHz to 24 MHz, which limits the | ||
| 233 | * allowed values for the pre-divider M (normal range 1-8). | ||
| 234 | * | ||
| 235 | * Fpfd = Fin / M | ||
| 236 | */ | ||
| 237 | m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000)); | ||
| 238 | m_max = min_t(unsigned int, 8, fin / 12000000); | ||
| 239 | |||
| 240 | for (m = m_min; m <= m_max; ++m) { | ||
| 241 | unsigned long fpfd; | ||
| 242 | unsigned int n_min; | ||
| 243 | unsigned int n_max; | ||
| 244 | unsigned int n; | ||
| 245 | |||
| 246 | /* | ||
| 247 | * The VCO operating range is 900 Mhz to 1800 MHz, which limits | ||
| 248 | * the allowed values for the multiplier N (normal range | ||
| 249 | * 60-120). | ||
| 250 | * | ||
| 251 | * Fvco = Fin * N / M | ||
| 252 | */ | ||
| 253 | fpfd = fin / m; | ||
| 254 | n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd)); | ||
| 255 | n_max = min_t(unsigned int, 120, 1800000000 / fpfd); | ||
| 256 | |||
| 257 | for (n = n_min; n < n_max; ++n) { | ||
| 258 | unsigned long fvco; | ||
| 259 | unsigned int e_min; | ||
| 260 | unsigned int e; | ||
| 261 | |||
| 262 | /* | ||
| 263 | * The output frequency is limited to 1039.5 MHz, | ||
| 264 | * limiting again the allowed values for the | ||
| 265 | * post-divider E (normal value 1, 2 or 4). | ||
| 266 | * | ||
| 267 | * Fout = Fvco / E | ||
| 268 | */ | ||
| 269 | fvco = fpfd * n; | ||
| 270 | e_min = fvco > 1039500000 ? 1 : 0; | ||
| 271 | |||
| 272 | for (e = e_min; e < 3; ++e) { | ||
| 273 | unsigned long fout; | ||
| 274 | unsigned long diff; | ||
| 275 | unsigned int div; | ||
| 276 | |||
| 277 | /* | ||
| 278 | * Finally we have a programable divider after | ||
| 279 | * the PLL, followed by a an optional fixed /7 | ||
| 280 | * divider. | ||
| 281 | */ | ||
| 282 | fout = fvco / (1 << e) / 7; | ||
| 283 | div = DIV_ROUND_CLOSEST(fout, target); | ||
| 284 | diff = abs(fout / div - target); | ||
| 285 | |||
| 286 | if (diff < pll->diff) { | ||
| 287 | pll->diff = diff; | ||
| 288 | pll->pll_m = m; | ||
| 289 | pll->pll_n = n; | ||
| 290 | pll->pll_e = e; | ||
| 291 | pll->div = div; | ||
| 292 | pll->clksel = clksel; | ||
| 293 | |||
| 294 | if (diff == 0) | ||
| 295 | goto done; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | done: | ||
| 302 | output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) | ||
| 303 | / 7 / pll->div; | ||
| 304 | error = (long)(output - target) * 10000 / (long)target; | ||
| 305 | |||
| 306 | dev_dbg(lvds->dev, | ||
| 307 | "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n", | ||
| 308 | clk, fin, output, target, error / 100, | ||
| 309 | error < 0 ? -error % 100 : error % 100, | ||
| 310 | pll->pll_m, pll->pll_n, pll->pll_e, pll->div); | ||
| 311 | } | ||
| 312 | |||
| 313 | static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) | ||
| 314 | { | ||
| 315 | struct pll_info pll = { .diff = (unsigned long)-1 }; | ||
| 316 | u32 lvdpllcr; | ||
| 317 | |||
| 318 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, | ||
| 319 | LVDPLLCR_CKSEL_DU_DOTCLKIN(0)); | ||
| 320 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, | ||
| 321 | LVDPLLCR_CKSEL_DU_DOTCLKIN(1)); | ||
| 322 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, | ||
| 323 | LVDPLLCR_CKSEL_EXTAL); | ||
| 324 | |||
| 325 | lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT | ||
| 326 | | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); | ||
| 327 | |||
| 328 | if (pll.pll_e > 0) | ||
| 329 | lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL | ||
| 330 | | LVDPLLCR_PLLE(pll.pll_e - 1); | ||
| 331 | |||
| 332 | rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); | ||
| 333 | |||
| 334 | if (pll.div > 1) | ||
| 335 | /* | ||
| 336 | * The DIVRESET bit is a misnomer, setting it to 1 deasserts the | ||
| 337 | * divisor reset. | ||
| 338 | */ | ||
| 339 | rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL | | ||
| 340 | LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1)); | ||
| 341 | else | ||
| 342 | rcar_lvds_write(lvds, LVDDIV, 0); | ||
| 343 | } | ||
| 344 | |||
| 345 | /* ----------------------------------------------------------------------------- | ||
| 346 | * Bridge | ||
| 347 | */ | ||
| 348 | |||
| 158 | static void rcar_lvds_enable(struct drm_bridge *bridge) | 349 | static void rcar_lvds_enable(struct drm_bridge *bridge) |
| 159 | { | 350 | { |
| 160 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | 351 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); |
| @@ -164,14 +355,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
| 164 | * do we get a state pointer? | 355 | * do we get a state pointer? |
| 165 | */ | 356 | */ |
| 166 | struct drm_crtc *crtc = lvds->bridge.encoder->crtc; | 357 | struct drm_crtc *crtc = lvds->bridge.encoder->crtc; |
| 167 | u32 lvdpllcr; | ||
| 168 | u32 lvdhcr; | 358 | u32 lvdhcr; |
| 169 | u32 lvdcr0; | 359 | u32 lvdcr0; |
| 170 | int ret; | 360 | int ret; |
| 171 | 361 | ||
| 172 | WARN_ON(lvds->enabled); | 362 | WARN_ON(lvds->enabled); |
| 173 | 363 | ||
| 174 | ret = clk_prepare_enable(lvds->clock); | 364 | ret = clk_prepare_enable(lvds->clocks.mod); |
| 175 | if (ret < 0) | 365 | if (ret < 0) |
| 176 | return; | 366 | return; |
| 177 | 367 | ||
| @@ -196,12 +386,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
| 196 | 386 | ||
| 197 | rcar_lvds_write(lvds, LVDCHCR, lvdhcr); | 387 | rcar_lvds_write(lvds, LVDCHCR, lvdhcr); |
| 198 | 388 | ||
| 389 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) { | ||
| 390 | /* Disable dual-link mode. */ | ||
| 391 | rcar_lvds_write(lvds, LVDSTRIPE, 0); | ||
| 392 | } | ||
| 393 | |||
| 199 | /* PLL clock configuration. */ | 394 | /* PLL clock configuration. */ |
| 200 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR) | 395 | lvds->info->pll_setup(lvds, mode->clock * 1000); |
| 201 | lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock); | ||
| 202 | else | ||
| 203 | lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock); | ||
| 204 | rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); | ||
| 205 | 396 | ||
| 206 | /* Set the LVDS mode and select the input. */ | 397 | /* Set the LVDS mode and select the input. */ |
| 207 | lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; | 398 | lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; |
| @@ -220,11 +411,16 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
| 220 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 411 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
| 221 | } | 412 | } |
| 222 | 413 | ||
| 223 | /* Turn the PLL on. */ | 414 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
| 224 | lvdcr0 |= LVDCR0_PLLON; | 415 | /* |
| 225 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 416 | * Turn the PLL on (simple PLL only, extended PLL is fully |
| 417 | * controlled through LVDPLLCR). | ||
| 418 | */ | ||
| 419 | lvdcr0 |= LVDCR0_PLLON; | ||
| 420 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | ||
| 421 | } | ||
| 226 | 422 | ||
| 227 | if (lvds->info->gen > 2) { | 423 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) { |
| 228 | /* Set LVDS normal mode. */ | 424 | /* Set LVDS normal mode. */ |
| 229 | lvdcr0 |= LVDCR0_PWD; | 425 | lvdcr0 |= LVDCR0_PWD; |
| 230 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 426 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
| @@ -236,8 +432,10 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
| 236 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 432 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
| 237 | } | 433 | } |
| 238 | 434 | ||
| 239 | /* Wait for the startup delay. */ | 435 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
| 240 | usleep_range(100, 150); | 436 | /* Wait for the PLL startup delay (simple PLL only). */ |
| 437 | usleep_range(100, 150); | ||
| 438 | } | ||
| 241 | 439 | ||
| 242 | /* Turn the output on. */ | 440 | /* Turn the output on. */ |
| 243 | lvdcr0 |= LVDCR0_LVRES; | 441 | lvdcr0 |= LVDCR0_LVRES; |
| @@ -264,8 +462,9 @@ static void rcar_lvds_disable(struct drm_bridge *bridge) | |||
| 264 | 462 | ||
| 265 | rcar_lvds_write(lvds, LVDCR0, 0); | 463 | rcar_lvds_write(lvds, LVDCR0, 0); |
| 266 | rcar_lvds_write(lvds, LVDCR1, 0); | 464 | rcar_lvds_write(lvds, LVDCR1, 0); |
| 465 | rcar_lvds_write(lvds, LVDPLLCR, 0); | ||
| 267 | 466 | ||
| 268 | clk_disable_unprepare(lvds->clock); | 467 | clk_disable_unprepare(lvds->clocks.mod); |
| 269 | 468 | ||
| 270 | lvds->enabled = false; | 469 | lvds->enabled = false; |
| 271 | } | 470 | } |
| @@ -446,6 +645,60 @@ done: | |||
| 446 | return ret; | 645 | return ret; |
| 447 | } | 646 | } |
| 448 | 647 | ||
| 648 | static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name, | ||
| 649 | bool optional) | ||
| 650 | { | ||
| 651 | struct clk *clk; | ||
| 652 | |||
| 653 | clk = devm_clk_get(lvds->dev, name); | ||
| 654 | if (!IS_ERR(clk)) | ||
| 655 | return clk; | ||
| 656 | |||
| 657 | if (PTR_ERR(clk) == -ENOENT && optional) | ||
| 658 | return NULL; | ||
| 659 | |||
| 660 | if (PTR_ERR(clk) != -EPROBE_DEFER) | ||
| 661 | dev_err(lvds->dev, "failed to get %s clock\n", | ||
| 662 | name ? name : "module"); | ||
| 663 | |||
| 664 | return clk; | ||
| 665 | } | ||
| 666 | |||
| 667 | static int rcar_lvds_get_clocks(struct rcar_lvds *lvds) | ||
| 668 | { | ||
| 669 | lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false); | ||
| 670 | if (IS_ERR(lvds->clocks.mod)) | ||
| 671 | return PTR_ERR(lvds->clocks.mod); | ||
| 672 | |||
| 673 | /* | ||
| 674 | * LVDS encoders without an extended PLL have no external clock inputs. | ||
| 675 | */ | ||
| 676 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) | ||
| 677 | return 0; | ||
| 678 | |||
| 679 | lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true); | ||
| 680 | if (IS_ERR(lvds->clocks.extal)) | ||
| 681 | return PTR_ERR(lvds->clocks.extal); | ||
| 682 | |||
| 683 | lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true); | ||
| 684 | if (IS_ERR(lvds->clocks.dotclkin[0])) | ||
| 685 | return PTR_ERR(lvds->clocks.dotclkin[0]); | ||
| 686 | |||
| 687 | lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true); | ||
| 688 | if (IS_ERR(lvds->clocks.dotclkin[1])) | ||
| 689 | return PTR_ERR(lvds->clocks.dotclkin[1]); | ||
| 690 | |||
| 691 | /* At least one input to the PLL must be available. */ | ||
| 692 | if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] && | ||
| 693 | !lvds->clocks.dotclkin[1]) { | ||
| 694 | dev_err(lvds->dev, | ||
| 695 | "no input clock (extal, dclkin.0 or dclkin.1)\n"); | ||
| 696 | return -EINVAL; | ||
| 697 | } | ||
| 698 | |||
| 699 | return 0; | ||
| 700 | } | ||
| 701 | |||
| 449 | static int rcar_lvds_probe(struct platform_device *pdev) | 702 | static int rcar_lvds_probe(struct platform_device *pdev) |
| 450 | { | 703 | { |
| 451 | struct rcar_lvds *lvds; | 704 | struct rcar_lvds *lvds; |
| @@ -475,11 +728,9 @@ static int rcar_lvds_probe(struct platform_device *pdev) | |||
| 475 | if (IS_ERR(lvds->mmio)) | 728 | if (IS_ERR(lvds->mmio)) |
| 476 | return PTR_ERR(lvds->mmio); | 729 | return PTR_ERR(lvds->mmio); |
| 477 | 730 | ||
| 478 | lvds->clock = devm_clk_get(&pdev->dev, NULL); | 731 | ret = rcar_lvds_get_clocks(lvds); |
| 479 | if (IS_ERR(lvds->clock)) { | 732 | if (ret < 0) |
| 480 | dev_err(&pdev->dev, "failed to get clock\n"); | 733 | return ret; |
| 481 | return PTR_ERR(lvds->clock); | ||
| 482 | } | ||
| 483 | 734 | ||
| 484 | drm_bridge_add(&lvds->bridge); | 735 | drm_bridge_add(&lvds->bridge); |
| 485 | 736 | ||
| @@ -497,21 +748,39 @@ static int rcar_lvds_remove(struct platform_device *pdev) | |||
| 497 | 748 | ||
| 498 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { | 749 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { |
| 499 | .gen = 2, | 750 | .gen = 2, |
| 500 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR, | 751 | .pll_setup = rcar_lvds_pll_setup_gen2, |
| 501 | }; | 752 | }; |
| 502 | 753 | ||
| 503 | static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = { | 754 | static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = { |
| 504 | .gen = 2, | 755 | .gen = 2, |
| 505 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_LANES, | 756 | .quirks = RCAR_LVDS_QUIRK_LANES, |
| 757 | .pll_setup = rcar_lvds_pll_setup_gen2, | ||
| 506 | }; | 758 | }; |
| 507 | 759 | ||
| 508 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { | 760 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { |
| 509 | .gen = 3, | 761 | .gen = 3, |
| 762 | .quirks = RCAR_LVDS_QUIRK_PWD, | ||
| 763 | .pll_setup = rcar_lvds_pll_setup_gen3, | ||
| 510 | }; | 764 | }; |
| 511 | 765 | ||
| 512 | static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { | 766 | static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { |
| 513 | .gen = 3, | 767 | .gen = 3, |
| 514 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_GEN3_LVEN, | 768 | .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN, |
| 769 | .pll_setup = rcar_lvds_pll_setup_gen2, | ||
| 770 | }; | ||
| 771 | |||
| 772 | static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = { | ||
| 773 | .gen = 3, | ||
| 774 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL | ||
| 775 | | RCAR_LVDS_QUIRK_DUAL_LINK, | ||
| 776 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | ||
| 777 | }; | ||
| 778 | |||
| 779 | static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { | ||
| 780 | .gen = 3, | ||
| 781 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD | ||
| 782 | | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, | ||
| 783 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | ||
| 515 | }; | 784 | }; |
| 516 | 785 | ||
| 517 | static const struct of_device_id rcar_lvds_of_table[] = { | 786 | static const struct of_device_id rcar_lvds_of_table[] = { |
| @@ -523,6 +792,8 @@ static const struct of_device_id rcar_lvds_of_table[] = { | |||
| 523 | { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, | 792 | { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, |
| 524 | { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, | 793 | { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, |
| 525 | { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, | 794 | { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, |
| 795 | { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, | ||
| 796 | { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info }, | ||
| 526 | { } | 797 | { } |
| 527 | }; | 798 | }; |
| 528 | 799 | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h index 4870f50d9bec..87149f2f8056 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | #define LVDCR0_PLLON (1 << 4) | 18 | #define LVDCR0_PLLON (1 << 4) |
| 19 | #define LVDCR0_PWD (1 << 2) /* Gen3 only */ | 19 | #define LVDCR0_PWD (1 << 2) /* Gen3 only */ |
| 20 | #define LVDCR0_BEN (1 << 2) /* Gen2 only */ | 20 | #define LVDCR0_BEN (1 << 2) /* Gen2 only */ |
| 21 | #define LVDCR0_LVEN (1 << 1) /* Gen2 only */ | 21 | #define LVDCR0_LVEN (1 << 1) |
| 22 | #define LVDCR0_LVRES (1 << 0) | 22 | #define LVDCR0_LVRES (1 << 0) |
| 23 | 23 | ||
| 24 | #define LVDCR1 0x0004 | 24 | #define LVDCR1 0x0004 |
| @@ -27,21 +27,36 @@ | |||
| 27 | #define LVDCR1_CLKSTBY (3 << 0) | 27 | #define LVDCR1_CLKSTBY (3 << 0) |
| 28 | 28 | ||
| 29 | #define LVDPLLCR 0x0008 | 29 | #define LVDPLLCR 0x0008 |
| 30 | /* Gen2 & V3M */ | ||
| 30 | #define LVDPLLCR_CEEN (1 << 14) | 31 | #define LVDPLLCR_CEEN (1 << 14) |
| 31 | #define LVDPLLCR_FBEN (1 << 13) | 32 | #define LVDPLLCR_FBEN (1 << 13) |
| 32 | #define LVDPLLCR_COSEL (1 << 12) | 33 | #define LVDPLLCR_COSEL (1 << 12) |
| 33 | /* Gen2 */ | ||
| 34 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) | 34 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) |
| 35 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) | 35 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) |
| 36 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) | 36 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) |
| 37 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) | 37 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) |
| 38 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) | 38 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) |
| 39 | /* Gen3 */ | 39 | /* Gen3 but V3M,D3 and E3 */ |
| 40 | #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) | 40 | #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) |
| 41 | #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) | 41 | #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) |
| 42 | #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) | 42 | #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) |
| 43 | #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) | 43 | #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) |
| 44 | #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) | 44 | #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) |
| 45 | /* D3 and E3 */ | ||
| 46 | #define LVDPLLCR_PLLON (1 << 22) | ||
| 47 | #define LVDPLLCR_PLLSEL_PLL0 (0 << 20) | ||
| 48 | #define LVDPLLCR_PLLSEL_LVX (1 << 20) | ||
| 49 | #define LVDPLLCR_PLLSEL_PLL1 (2 << 20) | ||
| 50 | #define LVDPLLCR_CKSEL_LVX (1 << 17) | ||
| 51 | #define LVDPLLCR_CKSEL_EXTAL (3 << 17) | ||
| 52 | #define LVDPLLCR_CKSEL_DU_DOTCLKIN(n) ((5 + (n) * 2) << 17) | ||
| 53 | #define LVDPLLCR_OCKSEL (1 << 16) | ||
| 54 | #define LVDPLLCR_STP_CLKOUTE (1 << 14) | ||
| 55 | #define LVDPLLCR_OUTCLKSEL (1 << 12) | ||
| 56 | #define LVDPLLCR_CLKOUT (1 << 11) | ||
| 57 | #define LVDPLLCR_PLLE(n) ((n) << 10) | ||
| 58 | #define LVDPLLCR_PLLN(n) ((n) << 3) | ||
| 59 | #define LVDPLLCR_PLLM(n) ((n) << 0) | ||
| 45 | 60 | ||
| 46 | #define LVDCTRCR 0x000c | 61 | #define LVDCTRCR 0x000c |
| 47 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) | 62 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) |
| @@ -71,4 +86,26 @@ | |||
| 71 | #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) | 86 | #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) |
| 72 | #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) | 87 | #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) |
| 73 | 88 | ||
| 89 | /* All registers below are specific to D3 and E3 */ | ||
| 90 | #define LVDSTRIPE 0x0014 | ||
| 91 | #define LVDSTRIPE_ST_TRGSEL_DISP (0 << 2) | ||
| 92 | #define LVDSTRIPE_ST_TRGSEL_HSYNC_R (1 << 2) | ||
| 93 | #define LVDSTRIPE_ST_TRGSEL_HSYNC_F (2 << 2) | ||
| 94 | #define LVDSTRIPE_ST_SWAP (1 << 1) | ||
| 95 | #define LVDSTRIPE_ST_ON (1 << 0) | ||
| 96 | |||
| 97 | #define LVDSCR 0x0018 | ||
| 98 | #define LVDSCR_DEPTH(n) (((n) - 1) << 29) | ||
| 99 | #define LVDSCR_BANDSET (1 << 28) | ||
| 100 | #define LVDSCR_TWGCNT(n) ((((n) - 256) / 16) << 24) | ||
| 101 | #define LVDSCR_SDIV(n) ((n) << 22) | ||
| 102 | #define LVDSCR_MODE (1 << 21) | ||
| 103 | #define LVDSCR_RSTN (1 << 20) | ||
| 104 | |||
| 105 | #define LVDDIV 0x001c | ||
| 106 | #define LVDDIV_DIVSEL (1 << 8) | ||
| 107 | #define LVDDIV_DIVRESET (1 << 7) | ||
| 108 | #define LVDDIV_DIVSTP (1 << 6) | ||
| 109 | #define LVDDIV_DIV(n) ((n) << 0) | ||
| 110 | |||
| 74 | #endif /* __RCAR_LVDS_REGS_H__ */ | 111 | #endif /* __RCAR_LVDS_REGS_H__ */ |
