| /* |
| * Copyright 2015 Freescale Semiconductor, Inc. |
| * |
| * Freescale DCU drm device driver |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/regmap.h> |
| |
| #include <drm/drmP.h> |
| #include <drm/drm_atomic.h> |
| #include <drm/drm_atomic_helper.h> |
| #include <drm/drm_crtc.h> |
| #include <drm/drm_crtc_helper.h> |
| |
| #include "fsl_dcu_drm_crtc.h" |
| #include "fsl_dcu_drm_drv.h" |
| #include "fsl_dcu_drm_plane.h" |
| |
| static void fsl_dcu_drm_crtc_atomic_begin(struct drm_crtc *crtc, |
| struct drm_crtc_state *old_crtc_state) |
| { |
| } |
| |
| static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc, |
| struct drm_crtc_state *state) |
| { |
| return 0; |
| } |
| |
| static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, |
| struct drm_crtc_state *old_crtc_state) |
| { |
| } |
| |
| static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) |
| { |
| struct drm_device *dev = crtc->dev; |
| struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; |
| int ret; |
| |
| ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, |
| DCU_MODE_DCU_MODE_MASK, |
| DCU_MODE_DCU_MODE(DCU_MODE_OFF)); |
| if (ret) |
| dev_err(fsl_dev->dev, "Disable CRTC failed\n"); |
| ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, |
| DCU_UPDATE_MODE_READREG); |
| if (ret) |
| dev_err(fsl_dev->dev, "Enable CRTC failed\n"); |
| } |
| |
| static void fsl_dcu_drm_crtc_enable(struct drm_crtc *crtc) |
| { |
| struct drm_device *dev = crtc->dev; |
| struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; |
| int ret; |
| |
| ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, |
| DCU_MODE_DCU_MODE_MASK, |
| DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); |
| if (ret) |
| dev_err(fsl_dev->dev, "Enable CRTC failed\n"); |
| ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, |
| DCU_UPDATE_MODE_READREG); |
| if (ret) |
| dev_err(fsl_dev->dev, "Enable CRTC failed\n"); |
| } |
| |
| static bool fsl_dcu_drm_crtc_mode_fixup(struct drm_crtc *crtc, |
| const struct drm_display_mode *mode, |
| struct drm_display_mode *adjusted_mode) |
| { |
| return true; |
| } |
| |
| static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) |
| { |
| struct drm_device *dev = crtc->dev; |
| struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; |
| struct drm_display_mode *mode = &crtc->state->mode; |
| unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index; |
| unsigned long dcuclk; |
| int ret; |
| |
| index = drm_crtc_index(crtc); |
| dcuclk = clk_get_rate(fsl_dev->clk); |
| div = dcuclk / mode->clock / 1000; |
| |
| /* Configure timings: */ |
| hbp = mode->htotal - mode->hsync_end; |
| hfp = mode->hsync_start - mode->hdisplay; |
| hsw = mode->hsync_end - mode->hsync_start; |
| vbp = mode->vtotal - mode->vsync_end; |
| vfp = mode->vsync_start - mode->vdisplay; |
| vsw = mode->vsync_end - mode->vsync_start; |
| |
| ret = regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, |
| DCU_HSYN_PARA_BP(hbp) | |
| DCU_HSYN_PARA_PW(hsw) | |
| DCU_HSYN_PARA_FP(hfp)); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, |
| DCU_VSYN_PARA_BP(vbp) | |
| DCU_VSYN_PARA_PW(vsw) | |
| DCU_VSYN_PARA_FP(vfp)); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, |
| DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | |
| DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_SYN_POL, |
| DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | |
| DCU_BGND_G(0) | DCU_BGND_B(0)); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_DCU_MODE, |
| DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_THRESHOLD, |
| DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | |
| DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | |
| DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); |
| if (ret) |
| goto set_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, |
| DCU_UPDATE_MODE_READREG); |
| if (ret) |
| goto set_failed; |
| return; |
| set_failed: |
| dev_err(dev->dev, "set DCU register failed\n"); |
| } |
| |
| static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { |
| .atomic_begin = fsl_dcu_drm_crtc_atomic_begin, |
| .atomic_check = fsl_dcu_drm_crtc_atomic_check, |
| .atomic_flush = fsl_dcu_drm_crtc_atomic_flush, |
| .disable = fsl_dcu_drm_disable_crtc, |
| .enable = fsl_dcu_drm_crtc_enable, |
| .mode_fixup = fsl_dcu_drm_crtc_mode_fixup, |
| .mode_set_nofb = fsl_dcu_drm_crtc_mode_set_nofb, |
| }; |
| |
| static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = { |
| .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
| .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
| .destroy = drm_crtc_cleanup, |
| .page_flip = drm_atomic_helper_page_flip, |
| .reset = drm_atomic_helper_crtc_reset, |
| .set_config = drm_atomic_helper_set_config, |
| }; |
| |
| int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev) |
| { |
| struct drm_plane *primary; |
| struct drm_crtc *crtc = &fsl_dev->crtc; |
| unsigned int i, j, reg_num; |
| int ret; |
| |
| primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm); |
| ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL, |
| &fsl_dcu_drm_crtc_funcs, NULL); |
| if (ret < 0) |
| return ret; |
| |
| drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs); |
| |
| if (!strcmp(fsl_dev->soc->name, "ls1021a")) |
| reg_num = LS1021A_LAYER_REG_NUM; |
| else |
| reg_num = VF610_LAYER_REG_NUM; |
| for (i = 0; i <= fsl_dev->soc->total_layer; i++) { |
| for (j = 0; j < reg_num; j++) { |
| ret = regmap_write(fsl_dev->regmap, |
| DCU_CTRLDESCLN(i, j), 0); |
| if (ret) |
| goto init_failed; |
| } |
| } |
| ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, |
| DCU_MODE_DCU_MODE_MASK, |
| DCU_MODE_DCU_MODE(DCU_MODE_OFF)); |
| if (ret) |
| goto init_failed; |
| ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, |
| DCU_UPDATE_MODE_READREG); |
| if (ret) |
| goto init_failed; |
| |
| return 0; |
| init_failed: |
| dev_err(fsl_dev->dev, "init DCU register failed\n"); |
| return ret; |
| } |