.. | .. |
---|
| 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, |
---|