| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2014 Traphandler |
|---|
| 3 | 4 | * Copyright (C) 2014 Free Electrons |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> |
|---|
| 6 | 7 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 10 | | - * the Free Software Foundation. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 13 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 14 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 15 | | - * more details. |
|---|
| 16 | | - * |
|---|
| 17 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 18 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 19 | 8 | */ |
|---|
| 20 | 9 | |
|---|
| 21 | 10 | #include <linux/clk.h> |
|---|
| 11 | +#include <linux/mfd/atmel-hlcdc.h> |
|---|
| 12 | +#include <linux/pinctrl/consumer.h> |
|---|
| 22 | 13 | #include <linux/pm.h> |
|---|
| 23 | 14 | #include <linux/pm_runtime.h> |
|---|
| 24 | | -#include <linux/pinctrl/consumer.h> |
|---|
| 25 | | - |
|---|
| 26 | | -#include <drm/drm_crtc.h> |
|---|
| 27 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 28 | | -#include <drm/drmP.h> |
|---|
| 29 | 15 | |
|---|
| 30 | 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> |
|---|
| 31 | 24 | |
|---|
| 32 | 25 | #include "atmel_hlcdc_dc.h" |
|---|
| 33 | 26 | |
|---|
| .. | .. |
|---|
| 78 | 71 | unsigned long mode_rate; |
|---|
| 79 | 72 | struct videomode vm; |
|---|
| 80 | 73 | unsigned long prate; |
|---|
| 81 | | - unsigned int cfg; |
|---|
| 74 | + unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; |
|---|
| 75 | + unsigned int cfg = 0; |
|---|
| 82 | 76 | int div, ret; |
|---|
| 83 | 77 | |
|---|
| 84 | 78 | ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); |
|---|
| .. | .. |
|---|
| 105 | 99 | (adj->crtc_hdisplay - 1) | |
|---|
| 106 | 100 | ((adj->crtc_vdisplay - 1) << 16)); |
|---|
| 107 | 101 | |
|---|
| 108 | | - cfg = 0; |
|---|
| 109 | | - |
|---|
| 110 | 102 | prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); |
|---|
| 111 | 103 | mode_rate = adj->crtc_clock * 1000; |
|---|
| 112 | | - if ((prate / 2) < mode_rate) { |
|---|
| 104 | + if (!crtc->dc->desc->fixed_clksrc) { |
|---|
| 113 | 105 | prate *= 2; |
|---|
| 114 | 106 | cfg |= ATMEL_HLCDC_CLKSEL; |
|---|
| 107 | + mask |= ATMEL_HLCDC_CLKSEL; |
|---|
| 115 | 108 | } |
|---|
| 116 | 109 | |
|---|
| 117 | 110 | div = DIV_ROUND_UP(prate, mode_rate); |
|---|
| 118 | | - if (div < 2) |
|---|
| 111 | + if (div < 2) { |
|---|
| 119 | 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 | + } |
|---|
| 120 | 133 | |
|---|
| 121 | 134 | cfg |= ATMEL_HLCDC_CLKDIV(div); |
|---|
| 122 | 135 | |
|---|
| 123 | | - regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), |
|---|
| 124 | | - ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | |
|---|
| 125 | | - ATMEL_HLCDC_CLKPOL, cfg); |
|---|
| 136 | + regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); |
|---|
| 126 | 137 | |
|---|
| 127 | | - cfg = 0; |
|---|
| 138 | + state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); |
|---|
| 139 | + cfg = state->output_mode << 8; |
|---|
| 128 | 140 | |
|---|
| 129 | 141 | if (adj->flags & DRM_MODE_FLAG_NVSYNC) |
|---|
| 130 | 142 | cfg |= ATMEL_HLCDC_VSPOL; |
|---|
| 131 | 143 | |
|---|
| 132 | 144 | if (adj->flags & DRM_MODE_FLAG_NHSYNC) |
|---|
| 133 | 145 | cfg |= ATMEL_HLCDC_HSPOL; |
|---|
| 134 | | - |
|---|
| 135 | | - state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); |
|---|
| 136 | | - cfg |= state->output_mode << 8; |
|---|
| 137 | 146 | |
|---|
| 138 | 147 | regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), |
|---|
| 139 | 148 | ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | |
|---|
| .. | .. |
|---|
| 232 | 241 | #define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) |
|---|
| 233 | 242 | #define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) |
|---|
| 234 | 243 | |
|---|
| 244 | +static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) |
|---|
| 245 | +{ |
|---|
| 246 | + struct drm_connector *connector = state->connector; |
|---|
| 247 | + struct drm_display_info *info = &connector->display_info; |
|---|
| 248 | + struct drm_encoder *encoder; |
|---|
| 249 | + unsigned int supported_fmts = 0; |
|---|
| 250 | + int j; |
|---|
| 251 | + |
|---|
| 252 | + encoder = state->best_encoder; |
|---|
| 253 | + if (!encoder) |
|---|
| 254 | + encoder = connector->encoder; |
|---|
| 255 | + |
|---|
| 256 | + switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { |
|---|
| 257 | + case 0: |
|---|
| 258 | + break; |
|---|
| 259 | + case MEDIA_BUS_FMT_RGB444_1X12: |
|---|
| 260 | + return ATMEL_HLCDC_RGB444_OUTPUT; |
|---|
| 261 | + case MEDIA_BUS_FMT_RGB565_1X16: |
|---|
| 262 | + return ATMEL_HLCDC_RGB565_OUTPUT; |
|---|
| 263 | + case MEDIA_BUS_FMT_RGB666_1X18: |
|---|
| 264 | + return ATMEL_HLCDC_RGB666_OUTPUT; |
|---|
| 265 | + case MEDIA_BUS_FMT_RGB888_1X24: |
|---|
| 266 | + return ATMEL_HLCDC_RGB888_OUTPUT; |
|---|
| 267 | + default: |
|---|
| 268 | + return -EINVAL; |
|---|
| 269 | + } |
|---|
| 270 | + |
|---|
| 271 | + for (j = 0; j < info->num_bus_formats; j++) { |
|---|
| 272 | + switch (info->bus_formats[j]) { |
|---|
| 273 | + case MEDIA_BUS_FMT_RGB444_1X12: |
|---|
| 274 | + supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; |
|---|
| 275 | + break; |
|---|
| 276 | + case MEDIA_BUS_FMT_RGB565_1X16: |
|---|
| 277 | + supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; |
|---|
| 278 | + break; |
|---|
| 279 | + case MEDIA_BUS_FMT_RGB666_1X18: |
|---|
| 280 | + supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; |
|---|
| 281 | + break; |
|---|
| 282 | + case MEDIA_BUS_FMT_RGB888_1X24: |
|---|
| 283 | + supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; |
|---|
| 284 | + break; |
|---|
| 285 | + default: |
|---|
| 286 | + break; |
|---|
| 287 | + } |
|---|
| 288 | + } |
|---|
| 289 | + |
|---|
| 290 | + return supported_fmts; |
|---|
| 291 | +} |
|---|
| 292 | + |
|---|
| 235 | 293 | static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) |
|---|
| 236 | 294 | { |
|---|
| 237 | 295 | unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; |
|---|
| .. | .. |
|---|
| 244 | 302 | crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); |
|---|
| 245 | 303 | |
|---|
| 246 | 304 | for_each_new_connector_in_state(state->state, connector, cstate, i) { |
|---|
| 247 | | - struct drm_display_info *info = &connector->display_info; |
|---|
| 248 | 305 | unsigned int supported_fmts = 0; |
|---|
| 249 | | - int j; |
|---|
| 250 | 306 | |
|---|
| 251 | 307 | if (!cstate->crtc) |
|---|
| 252 | 308 | continue; |
|---|
| 253 | 309 | |
|---|
| 254 | | - for (j = 0; j < info->num_bus_formats; j++) { |
|---|
| 255 | | - switch (info->bus_formats[j]) { |
|---|
| 256 | | - case MEDIA_BUS_FMT_RGB444_1X12: |
|---|
| 257 | | - supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; |
|---|
| 258 | | - break; |
|---|
| 259 | | - case MEDIA_BUS_FMT_RGB565_1X16: |
|---|
| 260 | | - supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; |
|---|
| 261 | | - break; |
|---|
| 262 | | - case MEDIA_BUS_FMT_RGB666_1X18: |
|---|
| 263 | | - supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; |
|---|
| 264 | | - break; |
|---|
| 265 | | - case MEDIA_BUS_FMT_RGB888_1X24: |
|---|
| 266 | | - supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; |
|---|
| 267 | | - break; |
|---|
| 268 | | - default: |
|---|
| 269 | | - break; |
|---|
| 270 | | - } |
|---|
| 271 | | - } |
|---|
| 310 | + supported_fmts = atmel_hlcdc_connector_output_mode(cstate); |
|---|
| 272 | 311 | |
|---|
| 273 | 312 | if (crtc->dc->desc->conflicting_output_formats) |
|---|
| 274 | 313 | output_fmts &= supported_fmts; |
|---|
| .. | .. |
|---|
| 324 | 363 | |
|---|
| 325 | 364 | static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { |
|---|
| 326 | 365 | .mode_valid = atmel_hlcdc_crtc_mode_valid, |
|---|
| 327 | | - .mode_set = drm_helper_crtc_mode_set, |
|---|
| 328 | 366 | .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, |
|---|
| 329 | | - .mode_set_base = drm_helper_crtc_mode_set_base, |
|---|
| 330 | 367 | .atomic_check = atmel_hlcdc_crtc_atomic_check, |
|---|
| 331 | 368 | .atomic_begin = atmel_hlcdc_crtc_atomic_begin, |
|---|
| 332 | 369 | .atomic_flush = atmel_hlcdc_crtc_atomic_flush, |
|---|
| .. | .. |
|---|
| 374 | 411 | } |
|---|
| 375 | 412 | |
|---|
| 376 | 413 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
|---|
| 377 | | - if (state) { |
|---|
| 378 | | - crtc->state = &state->base; |
|---|
| 379 | | - crtc->state->crtc = crtc; |
|---|
| 380 | | - } |
|---|
| 414 | + if (state) |
|---|
| 415 | + __drm_atomic_helper_crtc_reset(crtc, &state->base); |
|---|
| 381 | 416 | } |
|---|
| 382 | 417 | |
|---|
| 383 | 418 | static struct drm_crtc_state * |
|---|
| .. | .. |
|---|
| 491 | 526 | } |
|---|
| 492 | 527 | |
|---|
| 493 | 528 | drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); |
|---|
| 494 | | - drm_crtc_vblank_reset(&crtc->base); |
|---|
| 495 | 529 | |
|---|
| 496 | 530 | drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE); |
|---|
| 497 | 531 | drm_crtc_enable_color_mgmt(&crtc->base, 0, false, |
|---|