1 /* 2 * Copyright (C) 2014 Traphandler 3 * Copyright (C) 2014 Free Electrons 4 * 5 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 6 * 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 */ 20 21 #include <linux/clk.h> 22 #include <linux/pm.h> 23 #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 30 #include <video/videomode.h> 31 32 #include "atmel_hlcdc_dc.h" 33 34 /** 35 * Atmel HLCDC CRTC structure 36 * 37 * @base: base DRM CRTC structure 38 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device 39 * @event: pointer to the current page flip event 40 * @id: CRTC id (returned by drm_crtc_index) 41 * @enabled: CRTC state 42 */ 43 struct atmel_hlcdc_crtc { 44 struct drm_crtc base; 45 struct atmel_hlcdc_dc *dc; 46 struct drm_pending_vblank_event *event; 47 int id; 48 bool enabled; 49 }; 50 51 static inline struct atmel_hlcdc_crtc * 52 drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) 53 { 54 return container_of(crtc, struct atmel_hlcdc_crtc, base); 55 } 56 57 static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) 58 { 59 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 60 struct regmap *regmap = crtc->dc->hlcdc->regmap; 61 struct drm_display_mode *adj = &c->state->adjusted_mode; 62 unsigned long mode_rate; 63 struct videomode vm; 64 unsigned long prate; 65 unsigned int cfg; 66 int div; 67 68 vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; 69 vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; 70 vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; 71 vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; 72 vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; 73 vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; 74 75 regmap_write(regmap, ATMEL_HLCDC_CFG(1), 76 (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); 77 78 regmap_write(regmap, ATMEL_HLCDC_CFG(2), 79 (vm.vfront_porch - 1) | (vm.vback_porch << 16)); 80 81 regmap_write(regmap, ATMEL_HLCDC_CFG(3), 82 (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); 83 84 regmap_write(regmap, ATMEL_HLCDC_CFG(4), 85 (adj->crtc_hdisplay - 1) | 86 ((adj->crtc_vdisplay - 1) << 16)); 87 88 cfg = 0; 89 90 prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); 91 mode_rate = adj->crtc_clock * 1000; 92 if ((prate / 2) < mode_rate) { 93 prate *= 2; 94 cfg |= ATMEL_HLCDC_CLKSEL; 95 } 96 97 div = DIV_ROUND_UP(prate, mode_rate); 98 if (div < 2) 99 div = 2; 100 101 cfg |= ATMEL_HLCDC_CLKDIV(div); 102 103 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), 104 ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | 105 ATMEL_HLCDC_CLKPOL, cfg); 106 107 cfg = 0; 108 109 if (adj->flags & DRM_MODE_FLAG_NVSYNC) 110 cfg |= ATMEL_HLCDC_VSPOL; 111 112 if (adj->flags & DRM_MODE_FLAG_NHSYNC) 113 cfg |= ATMEL_HLCDC_HSPOL; 114 115 regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), 116 ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | 117 ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | 118 ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | 119 ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | 120 ATMEL_HLCDC_GUARDTIME_MASK, 121 cfg); 122 } 123 124 static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc, 125 const struct drm_display_mode *mode, 126 struct drm_display_mode *adjusted_mode) 127 { 128 return true; 129 } 130 131 static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) 132 { 133 struct drm_device *dev = c->dev; 134 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 135 struct regmap *regmap = crtc->dc->hlcdc->regmap; 136 unsigned int status; 137 138 if (!crtc->enabled) 139 return; 140 141 drm_crtc_vblank_off(c); 142 143 pm_runtime_get_sync(dev->dev); 144 145 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); 146 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 147 (status & ATMEL_HLCDC_DISP)) 148 cpu_relax(); 149 150 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); 151 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 152 (status & ATMEL_HLCDC_SYNC)) 153 cpu_relax(); 154 155 regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); 156 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 157 (status & ATMEL_HLCDC_PIXEL_CLK)) 158 cpu_relax(); 159 160 clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); 161 pinctrl_pm_select_sleep_state(dev->dev); 162 163 pm_runtime_allow(dev->dev); 164 165 pm_runtime_put_sync(dev->dev); 166 167 crtc->enabled = false; 168 } 169 170 static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) 171 { 172 struct drm_device *dev = c->dev; 173 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 174 struct regmap *regmap = crtc->dc->hlcdc->regmap; 175 unsigned int status; 176 177 if (crtc->enabled) 178 return; 179 180 pm_runtime_get_sync(dev->dev); 181 182 pm_runtime_forbid(dev->dev); 183 184 pinctrl_pm_select_default_state(dev->dev); 185 clk_prepare_enable(crtc->dc->hlcdc->sys_clk); 186 187 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); 188 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 189 !(status & ATMEL_HLCDC_PIXEL_CLK)) 190 cpu_relax(); 191 192 193 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); 194 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 195 !(status & ATMEL_HLCDC_SYNC)) 196 cpu_relax(); 197 198 regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); 199 while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && 200 !(status & ATMEL_HLCDC_DISP)) 201 cpu_relax(); 202 203 pm_runtime_put_sync(dev->dev); 204 205 drm_crtc_vblank_on(c); 206 207 crtc->enabled = true; 208 } 209 210 void atmel_hlcdc_crtc_suspend(struct drm_crtc *c) 211 { 212 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 213 214 if (crtc->enabled) { 215 atmel_hlcdc_crtc_disable(c); 216 /* save enable state for resume */ 217 crtc->enabled = true; 218 } 219 } 220 221 void atmel_hlcdc_crtc_resume(struct drm_crtc *c) 222 { 223 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 224 225 if (crtc->enabled) { 226 crtc->enabled = false; 227 atmel_hlcdc_crtc_enable(c); 228 } 229 } 230 231 static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, 232 struct drm_crtc_state *s) 233 { 234 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 235 236 if (atmel_hlcdc_dc_mode_valid(crtc->dc, &s->adjusted_mode) != MODE_OK) 237 return -EINVAL; 238 239 return atmel_hlcdc_plane_prepare_disc_area(s); 240 } 241 242 static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c, 243 struct drm_crtc_state *old_s) 244 { 245 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 246 247 if (c->state->event) { 248 c->state->event->pipe = drm_crtc_index(c); 249 250 WARN_ON(drm_crtc_vblank_get(c) != 0); 251 252 crtc->event = c->state->event; 253 c->state->event = NULL; 254 } 255 } 256 257 static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc, 258 struct drm_crtc_state *old_s) 259 { 260 /* TODO: write common plane control register if available */ 261 } 262 263 static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { 264 .mode_fixup = atmel_hlcdc_crtc_mode_fixup, 265 .mode_set = drm_helper_crtc_mode_set, 266 .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, 267 .mode_set_base = drm_helper_crtc_mode_set_base, 268 .disable = atmel_hlcdc_crtc_disable, 269 .enable = atmel_hlcdc_crtc_enable, 270 .atomic_check = atmel_hlcdc_crtc_atomic_check, 271 .atomic_begin = atmel_hlcdc_crtc_atomic_begin, 272 .atomic_flush = atmel_hlcdc_crtc_atomic_flush, 273 }; 274 275 static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) 276 { 277 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 278 279 drm_crtc_cleanup(c); 280 kfree(crtc); 281 } 282 283 void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c, 284 struct drm_file *file) 285 { 286 struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); 287 struct drm_pending_vblank_event *event; 288 struct drm_device *dev = c->dev; 289 unsigned long flags; 290 291 spin_lock_irqsave(&dev->event_lock, flags); 292 event = crtc->event; 293 if (event && event->base.file_priv == file) { 294 event->base.destroy(&event->base); 295 drm_vblank_put(dev, crtc->id); 296 crtc->event = NULL; 297 } 298 spin_unlock_irqrestore(&dev->event_lock, flags); 299 } 300 301 static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) 302 { 303 struct drm_device *dev = crtc->base.dev; 304 unsigned long flags; 305 306 spin_lock_irqsave(&dev->event_lock, flags); 307 if (crtc->event) { 308 drm_send_vblank_event(dev, crtc->id, crtc->event); 309 drm_vblank_put(dev, crtc->id); 310 crtc->event = NULL; 311 } 312 spin_unlock_irqrestore(&dev->event_lock, flags); 313 } 314 315 void atmel_hlcdc_crtc_irq(struct drm_crtc *c) 316 { 317 drm_handle_vblank(c->dev, 0); 318 atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); 319 } 320 321 static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { 322 .page_flip = drm_atomic_helper_page_flip, 323 .set_config = drm_atomic_helper_set_config, 324 .destroy = atmel_hlcdc_crtc_destroy, 325 .reset = drm_atomic_helper_crtc_reset, 326 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 327 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 328 }; 329 330 int atmel_hlcdc_crtc_create(struct drm_device *dev) 331 { 332 struct atmel_hlcdc_dc *dc = dev->dev_private; 333 struct atmel_hlcdc_planes *planes = dc->planes; 334 struct atmel_hlcdc_crtc *crtc; 335 int ret; 336 int i; 337 338 crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 339 if (!crtc) 340 return -ENOMEM; 341 342 crtc->dc = dc; 343 344 ret = drm_crtc_init_with_planes(dev, &crtc->base, 345 &planes->primary->base, 346 planes->cursor ? &planes->cursor->base : NULL, 347 &atmel_hlcdc_crtc_funcs); 348 if (ret < 0) 349 goto fail; 350 351 crtc->id = drm_crtc_index(&crtc->base); 352 353 if (planes->cursor) 354 planes->cursor->base.possible_crtcs = 1 << crtc->id; 355 356 for (i = 0; i < planes->noverlays; i++) 357 planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; 358 359 drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); 360 drm_crtc_vblank_reset(&crtc->base); 361 362 dc->crtc = &crtc->base; 363 364 return 0; 365 366 fail: 367 atmel_hlcdc_crtc_destroy(&crtc->base); 368 return ret; 369 } 370 371