atmel_hlcdc_crtc.c (14660B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2014 Traphandler 4 * Copyright (C) 2014 Free Electrons 5 * 6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 */ 9 10#include <linux/clk.h> 11#include <linux/mfd/atmel-hlcdc.h> 12#include <linux/pinctrl/consumer.h> 13#include <linux/pm.h> 14#include <linux/pm_runtime.h> 15 16#include <video/videomode.h> 17 18#include <drm/drm_atomic.h> 19#include <drm/drm_atomic_helper.h> 20#include <drm/drm_crtc.h> 21#include <drm/drm_modeset_helper_vtables.h> 22#include <drm/drm_probe_helper.h> 23#include <drm/drm_vblank.h> 24 25#include "atmel_hlcdc_dc.h" 26 27/** 28 * struct atmel_hlcdc_crtc_state - Atmel HLCDC CRTC state structure 29 * 30 * @base: base CRTC state 31 * @output_mode: RGBXXX output mode 32 */ 33struct atmel_hlcdc_crtc_state { 34 struct drm_crtc_state base; 35 unsigned int output_mode; 36}; 37 38static inline struct atmel_hlcdc_crtc_state * 39drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) 40{ 41 return container_of(state, struct atmel_hlcdc_crtc_state, base); 42} 43 44/** 45 * struct atmel_hlcdc_crtc - Atmel HLCDC CRTC structure 46 * 47 * @base: base DRM CRTC structure 48 * @dc: pointer to the atmel_hlcdc structure provided by the MFD device 49 * @event: pointer to the current page flip event 50 * @id: CRTC id (returned by drm_crtc_index) 51 */ 52struct atmel_hlcdc_crtc { 53 struct drm_crtc base; 54 struct atmel_hlcdc_dc *dc; 55 struct drm_pending_vblank_event *event; 56 int id; 57}; 58 59static inline struct atmel_hlcdc_crtc * 60drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) 61{ 62 return container_of(crtc, struct atmel_hlcdc_crtc, base); 63} 64 65static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) 66{ 67 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 68 struct regmap *regmap = crtc->dc->hlcdc->regmap; 69 struct drm_display_mode *adj = &c->state->adjusted_mode; 70 struct atmel_hlcdc_crtc_state *state; 71 unsigned long mode_rate; 72 struct videomode vm; 73 unsigned long prate; 74 unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; 75 unsigned int cfg = 0; 76 int div, ret; 77 78 ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 79 if (ret) 80 return; 81 82 vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; 83 vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; 84 vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; 85 vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; 86 vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; 87 vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; 88 89 regmap_write(regmap, ATMEL_HLCDC_CFG(1), 90 (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); 91 92 regmap_write(regmap, ATMEL_HLCDC_CFG(2), 93 (vm.vfront_porch - 1) | (vm.vback_porch << 16)); 94 95 regmap_write(regmap, ATMEL_HLCDC_CFG(3), 96 (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); 97 98 regmap_write(regmap, ATMEL_HLCDC_CFG(4), 99 (adj->crtc_hdisplay - 1) | 100 ((adj->crtc_vdisplay - 1) << 16)); 101 102 prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); 103 mode_rate = adj->crtc_clock * 1000; 104 if (!crtc->dc->desc->fixed_clksrc) { 105 prate *= 2; 106 cfg |= ATMEL_HLCDC_CLKSEL; 107 mask |= ATMEL_HLCDC_CLKSEL; 108 } 109 110 div = DIV_ROUND_UP(prate, mode_rate); 111 if (div < 2) { 112 div = 2; 113 } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { 114 /* The divider ended up too big, try a lower base rate. */ 115 cfg &= ~ATMEL_HLCDC_CLKSEL; 116 prate /= 2; 117 div = DIV_ROUND_UP(prate, mode_rate); 118 if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) 119 div = ATMEL_HLCDC_CLKDIV_MASK; 120 } else { 121 int div_low = prate / mode_rate; 122 123 if (div_low >= 2 && 124 (10 * (prate / div_low - mode_rate) < 125 (mode_rate - prate / div))) 126 /* 127 * At least 10 times better when using a higher 128 * frequency than requested, instead of a lower. 129 * So, go with that. 130 */ 131 div = div_low; 132 } 133 134 cfg |= ATMEL_HLCDC_CLKDIV(div); 135 136 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); 137 138 state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); 139 cfg = state->output_mode << 8; 140 141 if (adj->flags & DRM_MODE_FLAG_NVSYNC) 142 cfg |= ATMEL_HLCDC_VSPOL; 143 144 if (adj->flags & DRM_MODE_FLAG_NHSYNC) 145 cfg |= ATMEL_HLCDC_HSPOL; 146 147 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), 148 ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | 149 ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | 150 ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | 151 ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | 152 ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, 153 cfg); 154 155 clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 156} 157 158static enum drm_mode_status 159atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c, 160 const struct drm_display_mode *mode) 161{ 162 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 163 164 return atmel_hlcdc_dc_mode_valid(crtc->dc, mode); 165} 166 167static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c, 168 struct drm_atomic_state *state) 169{ 170 struct drm_device *dev = c->dev; 171 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 172 struct regmap *regmap = crtc->dc->hlcdc->regmap; 173 unsigned int status; 174 175 drm_crtc_vblank_off(c); 176 177 pm_runtime_get_sync(dev->dev); 178 179 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); 180 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 181 (status & ATMEL_HLCDC_DISP)) 182 cpu_relax(); 183 184 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); 185 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 186 (status & ATMEL_HLCDC_SYNC)) 187 cpu_relax(); 188 189 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); 190 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 191 (status & ATMEL_HLCDC_PIXEL_CLK)) 192 cpu_relax(); 193 194 clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 195 pinctrl_pm_select_sleep_state(dev->dev); 196 197 pm_runtime_allow(dev->dev); 198 199 pm_runtime_put_sync(dev->dev); 200} 201 202static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c, 203 struct drm_atomic_state *state) 204{ 205 struct drm_device *dev = c->dev; 206 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 207 struct regmap *regmap = crtc->dc->hlcdc->regmap; 208 unsigned int status; 209 210 pm_runtime_get_sync(dev->dev); 211 212 pm_runtime_forbid(dev->dev); 213 214 pinctrl_pm_select_default_state(dev->dev); 215 clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 216 217 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); 218 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 219 !(status & ATMEL_HLCDC_PIXEL_CLK)) 220 cpu_relax(); 221 222 223 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); 224 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 225 !(status & ATMEL_HLCDC_SYNC)) 226 cpu_relax(); 227 228 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); 229 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 230 !(status & ATMEL_HLCDC_DISP)) 231 cpu_relax(); 232 233 pm_runtime_put_sync(dev->dev); 234 235} 236 237#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) 238#define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) 239#define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) 240#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) 241#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) 242 243static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) 244{ 245 struct drm_connector *connector = state->connector; 246 struct drm_display_info *info = &connector->display_info; 247 struct drm_encoder *encoder; 248 unsigned int supported_fmts = 0; 249 int j; 250 251 encoder = state->best_encoder; 252 if (!encoder) 253 encoder = connector->encoder; 254 255 switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { 256 case 0: 257 break; 258 case MEDIA_BUS_FMT_RGB444_1X12: 259 return ATMEL_HLCDC_RGB444_OUTPUT; 260 case MEDIA_BUS_FMT_RGB565_1X16: 261 return ATMEL_HLCDC_RGB565_OUTPUT; 262 case MEDIA_BUS_FMT_RGB666_1X18: 263 return ATMEL_HLCDC_RGB666_OUTPUT; 264 case MEDIA_BUS_FMT_RGB888_1X24: 265 return ATMEL_HLCDC_RGB888_OUTPUT; 266 default: 267 return -EINVAL; 268 } 269 270 for (j = 0; j < info->num_bus_formats; j++) { 271 switch (info->bus_formats[j]) { 272 case MEDIA_BUS_FMT_RGB444_1X12: 273 supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; 274 break; 275 case MEDIA_BUS_FMT_RGB565_1X16: 276 supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; 277 break; 278 case MEDIA_BUS_FMT_RGB666_1X18: 279 supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; 280 break; 281 case MEDIA_BUS_FMT_RGB888_1X24: 282 supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; 283 break; 284 default: 285 break; 286 } 287 } 288 289 return supported_fmts; 290} 291 292static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) 293{ 294 unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; 295 struct atmel_hlcdc_crtc_state *hstate; 296 struct drm_connector_state *cstate; 297 struct drm_connector *connector; 298 struct atmel_hlcdc_crtc *crtc; 299 int i; 300 301 crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); 302 303 for_each_new_connector_in_state(state->state, connector, cstate, i) { 304 unsigned int supported_fmts = 0; 305 306 if (!cstate->crtc) 307 continue; 308 309 supported_fmts = atmel_hlcdc_connector_output_mode(cstate); 310 311 if (crtc->dc->desc->conflicting_output_formats) 312 output_fmts &= supported_fmts; 313 else 314 output_fmts |= supported_fmts; 315 } 316 317 if (!output_fmts) 318 return -EINVAL; 319 320 hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state); 321 hstate->output_mode = fls(output_fmts) - 1; 322 323 return 0; 324} 325 326static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, 327 struct drm_atomic_state *state) 328{ 329 struct drm_crtc_state *s = drm_atomic_get_new_crtc_state(state, c); 330 int ret; 331 332 ret = atmel_hlcdc_crtc_select_output_mode(s); 333 if (ret) 334 return ret; 335 336 ret = atmel_hlcdc_plane_prepare_disc_area(s); 337 if (ret) 338 return ret; 339 340 return atmel_hlcdc_plane_prepare_ahb_routing(s); 341} 342 343static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c, 344 struct drm_atomic_state *state) 345{ 346 drm_crtc_vblank_on(c); 347} 348 349static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *c, 350 struct drm_atomic_state *state) 351{ 352 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 353 unsigned long flags; 354 355 spin_lock_irqsave(&c->dev->event_lock, flags); 356 357 if (c->state->event) { 358 c->state->event->pipe = drm_crtc_index(c); 359 360 WARN_ON(drm_crtc_vblank_get(c) != 0); 361 362 crtc->event = c->state->event; 363 c->state->event = NULL; 364 } 365 spin_unlock_irqrestore(&c->dev->event_lock, flags); 366} 367 368static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { 369 .mode_valid = atmel_hlcdc_crtc_mode_valid, 370 .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, 371 .atomic_check = atmel_hlcdc_crtc_atomic_check, 372 .atomic_begin = atmel_hlcdc_crtc_atomic_begin, 373 .atomic_flush = atmel_hlcdc_crtc_atomic_flush, 374 .atomic_enable = atmel_hlcdc_crtc_atomic_enable, 375 .atomic_disable = atmel_hlcdc_crtc_atomic_disable, 376}; 377 378static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) 379{ 380 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 381 382 drm_crtc_cleanup(c); 383 kfree(crtc); 384} 385 386static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) 387{ 388 struct drm_device *dev = crtc->base.dev; 389 unsigned long flags; 390 391 spin_lock_irqsave(&dev->event_lock, flags); 392 if (crtc->event) { 393 drm_crtc_send_vblank_event(&crtc->base, crtc->event); 394 drm_crtc_vblank_put(&crtc->base); 395 crtc->event = NULL; 396 } 397 spin_unlock_irqrestore(&dev->event_lock, flags); 398} 399 400void atmel_hlcdc_crtc_irq(struct drm_crtc *c) 401{ 402 drm_crtc_handle_vblank(c); 403 atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); 404} 405 406static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) 407{ 408 struct atmel_hlcdc_crtc_state *state; 409 410 if (crtc->state) { 411 __drm_atomic_helper_crtc_destroy_state(crtc->state); 412 state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); 413 kfree(state); 414 crtc->state = NULL; 415 } 416 417 state = kzalloc(sizeof(*state), GFP_KERNEL); 418 if (state) 419 __drm_atomic_helper_crtc_reset(crtc, &state->base); 420} 421 422static struct drm_crtc_state * 423atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc) 424{ 425 struct atmel_hlcdc_crtc_state *state, *cur; 426 427 if (WARN_ON(!crtc->state)) 428 return NULL; 429 430 state = kmalloc(sizeof(*state), GFP_KERNEL); 431 if (!state) 432 return NULL; 433 __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); 434 435 cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); 436 state->output_mode = cur->output_mode; 437 438 return &state->base; 439} 440 441static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc, 442 struct drm_crtc_state *s) 443{ 444 struct atmel_hlcdc_crtc_state *state; 445 446 state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s); 447 __drm_atomic_helper_crtc_destroy_state(s); 448 kfree(state); 449} 450 451static int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c) 452{ 453 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 454 struct regmap *regmap = crtc->dc->hlcdc->regmap; 455 456 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 457 regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 458 459 return 0; 460} 461 462static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c) 463{ 464 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 465 struct regmap *regmap = crtc->dc->hlcdc->regmap; 466 467 regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 468} 469 470static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { 471 .page_flip = drm_atomic_helper_page_flip, 472 .set_config = drm_atomic_helper_set_config, 473 .destroy = atmel_hlcdc_crtc_destroy, 474 .reset = atmel_hlcdc_crtc_reset, 475 .atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state, 476 .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state, 477 .enable_vblank = atmel_hlcdc_crtc_enable_vblank, 478 .disable_vblank = atmel_hlcdc_crtc_disable_vblank, 479}; 480 481int atmel_hlcdc_crtc_create(struct drm_device *dev) 482{ 483 struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL; 484 struct atmel_hlcdc_dc *dc = dev->dev_private; 485 struct atmel_hlcdc_crtc *crtc; 486 int ret; 487 int i; 488 489 crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 490 if (!crtc) 491 return -ENOMEM; 492 493 crtc->dc = dc; 494 495 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 496 if (!dc->layers[i]) 497 continue; 498 499 switch (dc->layers[i]->desc->type) { 500 case ATMEL_HLCDC_BASE_LAYER: 501 primary = atmel_hlcdc_layer_to_plane(dc->layers[i]); 502 break; 503 504 case ATMEL_HLCDC_CURSOR_LAYER: 505 cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]); 506 break; 507 508 default: 509 break; 510 } 511 } 512 513 ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base, 514 &cursor->base, &atmel_hlcdc_crtc_funcs, 515 NULL); 516 if (ret < 0) 517 goto fail; 518 519 crtc->id = drm_crtc_index(&crtc->base); 520 521 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 522 struct atmel_hlcdc_plane *overlay; 523 524 if (dc->layers[i] && 525 dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) { 526 overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]); 527 overlay->base.possible_crtcs = 1 << crtc->id; 528 } 529 } 530 531 drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); 532 533 drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE); 534 drm_crtc_enable_color_mgmt(&crtc->base, 0, false, 535 ATMEL_HLCDC_CLUT_SIZE); 536 537 dc->crtc = &crtc->base; 538 539 return 0; 540 541fail: 542 atmel_hlcdc_crtc_destroy(&crtc->base); 543 return ret; 544}