2017-12-06 19:29:46 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2017-04-14 18:13:34 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) STMicroelectronics SA 2017
|
|
|
|
*
|
|
|
|
* Authors: Philippe Cornu <philippe.cornu@st.com>
|
|
|
|
* Yannick Fertre <yannick.fertre@st.com>
|
|
|
|
* Fabien Dessenne <fabien.dessenne@st.com>
|
|
|
|
* Mickael Reulier <mickael.reulier@st.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/component.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/interrupt.h>
|
2022-07-01 03:51:13 +08:00
|
|
|
#include <linux/media-bus-format.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <linux/module.h>
|
2017-04-14 18:13:34 +08:00
|
|
|
#include <linux/of_address.h>
|
|
|
|
#include <linux/of_graph.h>
|
2019-09-06 17:21:48 +08:00
|
|
|
#include <linux/pinctrl/consumer.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <linux/platform_device.h>
|
2019-06-03 16:32:02 +08:00
|
|
|
#include <linux/pm_runtime.h>
|
2021-12-16 05:47:38 +08:00
|
|
|
#include <linux/regmap.h>
|
2017-04-14 18:13:34 +08:00
|
|
|
#include <linux/reset.h>
|
|
|
|
|
|
|
|
#include <drm/drm_atomic.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
2022-06-14 04:03:12 +08:00
|
|
|
#include <drm/drm_blend.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <drm/drm_bridge.h>
|
|
|
|
#include <drm/drm_device.h>
|
2022-06-14 17:02:45 +08:00
|
|
|
#include <drm/drm_edid.h>
|
2022-08-02 08:04:02 +08:00
|
|
|
#include <drm/drm_fb_dma_helper.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <drm/drm_fourcc.h>
|
2022-06-14 17:54:49 +08:00
|
|
|
#include <drm/drm_framebuffer.h>
|
2021-02-22 22:17:56 +08:00
|
|
|
#include <drm/drm_gem_atomic_helper.h>
|
2022-08-02 08:04:03 +08:00
|
|
|
#include <drm/drm_gem_dma_helper.h>
|
2017-04-14 18:13:34 +08:00
|
|
|
#include <drm/drm_of.h>
|
2019-01-18 05:03:34 +08:00
|
|
|
#include <drm/drm_probe_helper.h>
|
2021-03-03 01:57:00 +08:00
|
|
|
#include <drm/drm_simple_kms_helper.h>
|
2019-01-19 16:40:09 +08:00
|
|
|
#include <drm/drm_vblank.h>
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
#include <video/videomode.h>
|
|
|
|
|
|
|
|
#include "ltdc.h"
|
|
|
|
|
|
|
|
#define NB_CRTC 1
|
|
|
|
#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
|
|
|
|
|
|
|
|
#define MAX_IRQ 4
|
|
|
|
|
|
|
|
#define HWVER_10200 0x010200
|
|
|
|
#define HWVER_10300 0x010300
|
|
|
|
#define HWVER_20101 0x020101
|
2021-12-03 16:56:18 +08:00
|
|
|
#define HWVER_40100 0x040100
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The address of some registers depends on the HW version: such registers have
|
2021-12-03 16:56:18 +08:00
|
|
|
* an extra offset specified with layer_ofs.
|
2017-04-14 18:13:34 +08:00
|
|
|
*/
|
2021-12-03 16:56:18 +08:00
|
|
|
#define LAY_OFS_0 0x80
|
|
|
|
#define LAY_OFS_1 0x100
|
|
|
|
#define LAY_OFS (ldev->caps.layer_ofs)
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Global register offsets */
|
2017-07-20 20:05:53 +08:00
|
|
|
#define LTDC_IDR 0x0000 /* IDentification */
|
|
|
|
#define LTDC_LCR 0x0004 /* Layer Count */
|
|
|
|
#define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */
|
|
|
|
#define LTDC_BPCR 0x000C /* Back Porch Configuration */
|
|
|
|
#define LTDC_AWCR 0x0010 /* Active Width Configuration */
|
|
|
|
#define LTDC_TWCR 0x0014 /* Total Width Configuration */
|
|
|
|
#define LTDC_GCR 0x0018 /* Global Control */
|
|
|
|
#define LTDC_GC1R 0x001C /* Global Configuration 1 */
|
|
|
|
#define LTDC_GC2R 0x0020 /* Global Configuration 2 */
|
|
|
|
#define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */
|
|
|
|
#define LTDC_GACR 0x0028 /* GAmma Correction */
|
|
|
|
#define LTDC_BCCR 0x002C /* Background Color Configuration */
|
|
|
|
#define LTDC_IER 0x0034 /* Interrupt Enable */
|
|
|
|
#define LTDC_ISR 0x0038 /* Interrupt Status */
|
|
|
|
#define LTDC_ICR 0x003C /* Interrupt Clear */
|
|
|
|
#define LTDC_LIPCR 0x0040 /* Line Interrupt Position Conf. */
|
|
|
|
#define LTDC_CPSR 0x0044 /* Current Position Status */
|
|
|
|
#define LTDC_CDSR 0x0048 /* Current Display Status */
|
2021-12-16 05:47:50 +08:00
|
|
|
#define LTDC_EDCR 0x0060 /* External Display Control */
|
2022-02-11 18:46:20 +08:00
|
|
|
#define LTDC_CCRCR 0x007C /* Computed CRC value */
|
2021-12-03 16:56:18 +08:00
|
|
|
#define LTDC_FUT 0x0090 /* Fifo underrun Threshold */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Layer register offsets */
|
2021-12-03 16:56:18 +08:00
|
|
|
#define LTDC_L1C0R (ldev->caps.layer_regs[0]) /* L1 configuration 0 */
|
|
|
|
#define LTDC_L1C1R (ldev->caps.layer_regs[1]) /* L1 configuration 1 */
|
|
|
|
#define LTDC_L1RCR (ldev->caps.layer_regs[2]) /* L1 reload control */
|
|
|
|
#define LTDC_L1CR (ldev->caps.layer_regs[3]) /* L1 control register */
|
|
|
|
#define LTDC_L1WHPCR (ldev->caps.layer_regs[4]) /* L1 window horizontal position configuration */
|
|
|
|
#define LTDC_L1WVPCR (ldev->caps.layer_regs[5]) /* L1 window vertical position configuration */
|
|
|
|
#define LTDC_L1CKCR (ldev->caps.layer_regs[6]) /* L1 color keying configuration */
|
|
|
|
#define LTDC_L1PFCR (ldev->caps.layer_regs[7]) /* L1 pixel format configuration */
|
|
|
|
#define LTDC_L1CACR (ldev->caps.layer_regs[8]) /* L1 constant alpha configuration */
|
|
|
|
#define LTDC_L1DCCR (ldev->caps.layer_regs[9]) /* L1 default color configuration */
|
|
|
|
#define LTDC_L1BFCR (ldev->caps.layer_regs[10]) /* L1 blending factors configuration */
|
|
|
|
#define LTDC_L1BLCR (ldev->caps.layer_regs[11]) /* L1 burst length configuration */
|
|
|
|
#define LTDC_L1PCR (ldev->caps.layer_regs[12]) /* L1 planar configuration */
|
|
|
|
#define LTDC_L1CFBAR (ldev->caps.layer_regs[13]) /* L1 color frame buffer address */
|
|
|
|
#define LTDC_L1CFBLR (ldev->caps.layer_regs[14]) /* L1 color frame buffer length */
|
|
|
|
#define LTDC_L1CFBLNR (ldev->caps.layer_regs[15]) /* L1 color frame buffer line number */
|
|
|
|
#define LTDC_L1AFBA0R (ldev->caps.layer_regs[16]) /* L1 auxiliary frame buffer address 0 */
|
|
|
|
#define LTDC_L1AFBA1R (ldev->caps.layer_regs[17]) /* L1 auxiliary frame buffer address 1 */
|
|
|
|
#define LTDC_L1AFBLR (ldev->caps.layer_regs[18]) /* L1 auxiliary frame buffer length */
|
|
|
|
#define LTDC_L1AFBLNR (ldev->caps.layer_regs[19]) /* L1 auxiliary frame buffer line number */
|
|
|
|
#define LTDC_L1CLUTWR (ldev->caps.layer_regs[20]) /* L1 CLUT write */
|
|
|
|
#define LTDC_L1CYR0R (ldev->caps.layer_regs[21]) /* L1 Conversion YCbCr RGB 0 */
|
|
|
|
#define LTDC_L1CYR1R (ldev->caps.layer_regs[22]) /* L1 Conversion YCbCr RGB 1 */
|
|
|
|
#define LTDC_L1FPF0R (ldev->caps.layer_regs[23]) /* L1 Flexible Pixel Format 0 */
|
|
|
|
#define LTDC_L1FPF1R (ldev->caps.layer_regs[24]) /* L1 Flexible Pixel Format 1 */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Bit definitions */
|
|
|
|
#define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */
|
|
|
|
#define SSCR_HSW GENMASK(27, 16) /* Horizontal Synchronization Width */
|
|
|
|
|
|
|
|
#define BPCR_AVBP GENMASK(10, 0) /* Accumulated Vertical Back Porch */
|
|
|
|
#define BPCR_AHBP GENMASK(27, 16) /* Accumulated Horizontal Back Porch */
|
|
|
|
|
|
|
|
#define AWCR_AAH GENMASK(10, 0) /* Accumulated Active Height */
|
|
|
|
#define AWCR_AAW GENMASK(27, 16) /* Accumulated Active Width */
|
|
|
|
|
|
|
|
#define TWCR_TOTALH GENMASK(10, 0) /* TOTAL Height */
|
|
|
|
#define TWCR_TOTALW GENMASK(27, 16) /* TOTAL Width */
|
|
|
|
|
|
|
|
#define GCR_LTDCEN BIT(0) /* LTDC ENable */
|
|
|
|
#define GCR_DEN BIT(16) /* Dither ENable */
|
2022-02-11 18:46:20 +08:00
|
|
|
#define GCR_CRCEN BIT(19) /* CRC ENable */
|
2017-07-20 20:05:52 +08:00
|
|
|
#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity-Inverted */
|
|
|
|
#define GCR_DEPOL BIT(29) /* Data Enable POLarity-High */
|
|
|
|
#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity-High */
|
|
|
|
#define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity-High */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
#define GC1R_WBCH GENMASK(3, 0) /* Width of Blue CHannel output */
|
|
|
|
#define GC1R_WGCH GENMASK(7, 4) /* Width of Green Channel output */
|
|
|
|
#define GC1R_WRCH GENMASK(11, 8) /* Width of Red Channel output */
|
|
|
|
#define GC1R_PBEN BIT(12) /* Precise Blending ENable */
|
|
|
|
#define GC1R_DT GENMASK(15, 14) /* Dithering Technique */
|
|
|
|
#define GC1R_GCT GENMASK(19, 17) /* Gamma Correction Technique */
|
|
|
|
#define GC1R_SHREN BIT(21) /* SHadow Registers ENabled */
|
|
|
|
#define GC1R_BCP BIT(22) /* Background Colour Programmable */
|
|
|
|
#define GC1R_BBEN BIT(23) /* Background Blending ENabled */
|
|
|
|
#define GC1R_LNIP BIT(24) /* Line Number IRQ Position */
|
|
|
|
#define GC1R_TP BIT(25) /* Timing Programmable */
|
|
|
|
#define GC1R_IPP BIT(26) /* IRQ Polarity Programmable */
|
|
|
|
#define GC1R_SPP BIT(27) /* Sync Polarity Programmable */
|
|
|
|
#define GC1R_DWP BIT(28) /* Dither Width Programmable */
|
|
|
|
#define GC1R_STREN BIT(29) /* STatus Registers ENabled */
|
|
|
|
#define GC1R_BMEN BIT(31) /* Blind Mode ENabled */
|
|
|
|
|
|
|
|
#define GC2R_EDCA BIT(0) /* External Display Control Ability */
|
|
|
|
#define GC2R_STSAEN BIT(1) /* Slave Timing Sync Ability ENabled */
|
|
|
|
#define GC2R_DVAEN BIT(2) /* Dual-View Ability ENabled */
|
|
|
|
#define GC2R_DPAEN BIT(3) /* Dual-Port Ability ENabled */
|
|
|
|
#define GC2R_BW GENMASK(6, 4) /* Bus Width (log2 of nb of bytes) */
|
|
|
|
#define GC2R_EDCEN BIT(7) /* External Display Control ENabled */
|
|
|
|
|
|
|
|
#define SRCR_IMR BIT(0) /* IMmediate Reload */
|
|
|
|
#define SRCR_VBR BIT(1) /* Vertical Blanking Reload */
|
|
|
|
|
|
|
|
#define BCCR_BCBLACK 0x00 /* Background Color BLACK */
|
|
|
|
#define BCCR_BCBLUE GENMASK(7, 0) /* Background Color BLUE */
|
|
|
|
#define BCCR_BCGREEN GENMASK(15, 8) /* Background Color GREEN */
|
|
|
|
#define BCCR_BCRED GENMASK(23, 16) /* Background Color RED */
|
|
|
|
#define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
|
|
|
|
|
|
|
|
#define IER_LIE BIT(0) /* Line Interrupt Enable */
|
2022-06-03 21:46:54 +08:00
|
|
|
#define IER_FUWIE BIT(1) /* Fifo Underrun Warning Interrupt Enable */
|
2017-04-14 18:13:34 +08:00
|
|
|
#define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */
|
2022-06-03 21:46:54 +08:00
|
|
|
#define IER_RRIE BIT(3) /* Register Reload Interrupt Enable */
|
|
|
|
#define IER_FUEIE BIT(6) /* Fifo Underrun Error Interrupt Enable */
|
|
|
|
#define IER_CRCIE BIT(7) /* CRC Error Interrupt Enable */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2018-06-29 21:01:40 +08:00
|
|
|
#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
#define ISR_LIF BIT(0) /* Line Interrupt Flag */
|
2022-06-03 21:46:54 +08:00
|
|
|
#define ISR_FUWIF BIT(1) /* Fifo Underrun Warning Interrupt Flag */
|
2017-04-14 18:13:34 +08:00
|
|
|
#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
|
|
|
|
#define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */
|
2022-06-03 21:46:54 +08:00
|
|
|
#define ISR_FUEIF BIT(6) /* Fifo Underrun Error Interrupt Flag */
|
|
|
|
#define ISR_CRCIF BIT(7) /* CRC Error Interrupt Flag */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:47:50 +08:00
|
|
|
#define EDCR_OCYEN BIT(25) /* Output Conversion to YCbCr 422: ENable */
|
|
|
|
#define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */
|
|
|
|
#define EDCR_OCYCO BIT(27) /* Output Conversion to YCbCr 422: Chrominance Order */
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
#define LXCR_LEN BIT(0) /* Layer ENable */
|
|
|
|
#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
|
|
|
|
#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
|
2022-06-03 21:45:47 +08:00
|
|
|
#define LXCR_HMEN BIT(8) /* Horizontal Mirroring ENable */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
|
|
|
|
#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
|
|
|
|
|
|
|
|
#define LXWVPCR_WVSTPOS GENMASK(10, 0) /* Window Vertical StarT POSition */
|
|
|
|
#define LXWVPCR_WVSPPOS GENMASK(26, 16) /* Window Vertical StoP POSition */
|
|
|
|
|
|
|
|
#define LXPFCR_PF GENMASK(2, 0) /* Pixel Format */
|
2021-12-16 05:48:35 +08:00
|
|
|
#define PF_FLEXIBLE 0x7 /* Flexible Pixel Format selected */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
#define LXCACR_CONSTA GENMASK(7, 0) /* CONSTant Alpha */
|
|
|
|
|
|
|
|
#define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */
|
|
|
|
#define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */
|
2022-06-03 21:44:59 +08:00
|
|
|
#define LXBFCR_BOR GENMASK(18, 16) /* Blending ORder */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
|
2022-06-03 21:45:47 +08:00
|
|
|
#define LXCFBLR_CFBP GENMASK(31, 16) /* Color Frame Buffer Pitch in bytes */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2017-07-20 20:05:53 +08:00
|
|
|
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
#define LXCR_C1R_YIA BIT(0) /* Ycbcr 422 Interleaved Ability */
|
|
|
|
#define LXCR_C1R_YSPA BIT(1) /* Ycbcr 420 Semi-Planar Ability */
|
|
|
|
#define LXCR_C1R_YFPA BIT(2) /* Ycbcr 420 Full-Planar Ability */
|
|
|
|
#define LXCR_C1R_SCA BIT(31) /* SCaling Ability*/
|
|
|
|
|
|
|
|
#define LxPCR_YREN BIT(9) /* Y Rescale Enable for the color dynamic range */
|
|
|
|
#define LxPCR_OF BIT(8) /* Odd pixel First */
|
|
|
|
#define LxPCR_CBF BIT(7) /* CB component First */
|
|
|
|
#define LxPCR_YF BIT(6) /* Y component First */
|
|
|
|
#define LxPCR_YCM GENMASK(5, 4) /* Ycbcr Conversion Mode */
|
|
|
|
#define YCM_I 0x0 /* Interleaved 422 */
|
|
|
|
#define YCM_SP 0x1 /* Semi-Planar 420 */
|
|
|
|
#define YCM_FP 0x2 /* Full-Planar 420 */
|
|
|
|
#define LxPCR_YCEN BIT(3) /* YCbCr-to-RGB Conversion Enable */
|
|
|
|
|
2021-12-16 05:48:17 +08:00
|
|
|
#define LXRCR_IMR BIT(0) /* IMmediate Reload */
|
|
|
|
#define LXRCR_VBR BIT(1) /* Vertical Blanking Reload */
|
|
|
|
#define LXRCR_GRMSK BIT(2) /* Global (centralized) Reload MaSKed */
|
|
|
|
|
2017-10-26 19:17:46 +08:00
|
|
|
#define CLUT_SIZE 256
|
|
|
|
|
2017-07-20 20:05:53 +08:00
|
|
|
#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */
|
|
|
|
#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */
|
|
|
|
#define BF1_CA 0x400 /* Constant Alpha */
|
|
|
|
#define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */
|
|
|
|
#define BF2_1CA 0x005 /* 1 - Constant Alpha */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2017-07-20 20:05:53 +08:00
|
|
|
#define NB_PF 8 /* Max nb of HW pixel format */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2022-06-03 21:46:54 +08:00
|
|
|
#define FUT_DFT 128 /* Default value of fifo underrun threshold */
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
/*
|
|
|
|
* Skip the first value and the second in case CRC was enabled during
|
|
|
|
* the thread irq. This is to be sure CRC value is relevant for the
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
#define CRC_SKIP_FRAMES 2
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
enum ltdc_pix_fmt {
|
|
|
|
PF_NONE,
|
|
|
|
/* RGB formats */
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_ARGB8888, /* ARGB [32 bits] */
|
|
|
|
PF_RGBA8888, /* RGBA [32 bits] */
|
2021-12-16 05:48:35 +08:00
|
|
|
PF_ABGR8888, /* ABGR [32 bits] */
|
|
|
|
PF_BGRA8888, /* BGRA [32 bits] */
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_RGB888, /* RGB [24 bits] */
|
2021-12-16 05:48:35 +08:00
|
|
|
PF_BGR888, /* BGR [24 bits] */
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_RGB565, /* RGB [16 bits] */
|
2021-12-16 05:48:35 +08:00
|
|
|
PF_BGR565, /* BGR [16 bits] */
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */
|
|
|
|
PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
|
2017-04-14 18:13:34 +08:00
|
|
|
/* Indexed formats */
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_L8, /* Indexed 8 bits [8 bits] */
|
|
|
|
PF_AL44, /* Alpha:4 bits + indexed 4 bits [8 bits] */
|
2021-12-16 05:48:35 +08:00
|
|
|
PF_AL88 /* Alpha:8 bits + indexed 8 bits [16 bits] */
|
2017-04-14 18:13:34 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* The index gives the encoding of the pixel format for an HW version */
|
|
|
|
static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_ARGB8888, /* 0x00 */
|
|
|
|
PF_RGB888, /* 0x01 */
|
|
|
|
PF_RGB565, /* 0x02 */
|
|
|
|
PF_ARGB1555, /* 0x03 */
|
|
|
|
PF_ARGB4444, /* 0x04 */
|
|
|
|
PF_L8, /* 0x05 */
|
|
|
|
PF_AL44, /* 0x06 */
|
|
|
|
PF_AL88 /* 0x07 */
|
2017-04-14 18:13:34 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
|
2017-07-20 20:05:53 +08:00
|
|
|
PF_ARGB8888, /* 0x00 */
|
|
|
|
PF_RGB888, /* 0x01 */
|
|
|
|
PF_RGB565, /* 0x02 */
|
|
|
|
PF_RGBA8888, /* 0x03 */
|
|
|
|
PF_AL44, /* 0x04 */
|
|
|
|
PF_L8, /* 0x05 */
|
|
|
|
PF_ARGB1555, /* 0x06 */
|
|
|
|
PF_ARGB4444 /* 0x07 */
|
2017-04-14 18:13:34 +08:00
|
|
|
};
|
|
|
|
|
2021-12-03 16:56:18 +08:00
|
|
|
static const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
|
|
|
|
PF_ARGB8888, /* 0x00 */
|
|
|
|
PF_ABGR8888, /* 0x01 */
|
|
|
|
PF_RGBA8888, /* 0x02 */
|
|
|
|
PF_BGRA8888, /* 0x03 */
|
|
|
|
PF_RGB565, /* 0x04 */
|
|
|
|
PF_BGR565, /* 0x05 */
|
|
|
|
PF_RGB888, /* 0x06 */
|
2021-12-16 05:48:35 +08:00
|
|
|
PF_NONE /* 0x07 */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_drm_fmt_a0[] = {
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_RGB888,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_XRGB4444,
|
|
|
|
DRM_FORMAT_C8
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_drm_fmt_a1[] = {
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_RGB888,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_RGBX8888,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_XRGB4444,
|
|
|
|
DRM_FORMAT_C8
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_drm_fmt_a2[] = {
|
|
|
|
DRM_FORMAT_ARGB8888,
|
|
|
|
DRM_FORMAT_XRGB8888,
|
|
|
|
DRM_FORMAT_ABGR8888,
|
|
|
|
DRM_FORMAT_XBGR8888,
|
|
|
|
DRM_FORMAT_RGBA8888,
|
|
|
|
DRM_FORMAT_RGBX8888,
|
|
|
|
DRM_FORMAT_BGRA8888,
|
|
|
|
DRM_FORMAT_BGRX8888,
|
|
|
|
DRM_FORMAT_RGB565,
|
|
|
|
DRM_FORMAT_BGR565,
|
|
|
|
DRM_FORMAT_RGB888,
|
|
|
|
DRM_FORMAT_BGR888,
|
|
|
|
DRM_FORMAT_ARGB1555,
|
|
|
|
DRM_FORMAT_XRGB1555,
|
|
|
|
DRM_FORMAT_ARGB4444,
|
|
|
|
DRM_FORMAT_XRGB4444,
|
|
|
|
DRM_FORMAT_C8
|
2021-12-03 16:56:18 +08:00
|
|
|
};
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
|
|
|
|
DRM_FORMAT_YUYV,
|
|
|
|
DRM_FORMAT_YVYU,
|
|
|
|
DRM_FORMAT_UYVY,
|
|
|
|
DRM_FORMAT_VYUY
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_drm_fmt_ycbcr_sp[] = {
|
|
|
|
DRM_FORMAT_NV12,
|
|
|
|
DRM_FORMAT_NV21
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_drm_fmt_ycbcr_fp[] = {
|
|
|
|
DRM_FORMAT_YUV420,
|
|
|
|
DRM_FORMAT_YVU420
|
|
|
|
};
|
|
|
|
|
2021-12-03 16:56:18 +08:00
|
|
|
/* Layer register offsets */
|
|
|
|
static const u32 ltdc_layer_regs_a0[] = {
|
|
|
|
0x80, /* L1 configuration 0 */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x84, /* L1 control register */
|
|
|
|
0x88, /* L1 window horizontal position configuration */
|
|
|
|
0x8c, /* L1 window vertical position configuration */
|
|
|
|
0x90, /* L1 color keying configuration */
|
|
|
|
0x94, /* L1 pixel format configuration */
|
|
|
|
0x98, /* L1 constant alpha configuration */
|
|
|
|
0x9c, /* L1 default color configuration */
|
|
|
|
0xa0, /* L1 blending factors configuration */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0xac, /* L1 color frame buffer address */
|
|
|
|
0xb0, /* L1 color frame buffer length */
|
|
|
|
0xb4, /* L1 color frame buffer line number */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0xc4, /* L1 CLUT write */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00 /* not available */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_layer_regs_a1[] = {
|
|
|
|
0x80, /* L1 configuration 0 */
|
|
|
|
0x84, /* L1 configuration 1 */
|
|
|
|
0x00, /* L1 reload control */
|
|
|
|
0x88, /* L1 control register */
|
|
|
|
0x8c, /* L1 window horizontal position configuration */
|
|
|
|
0x90, /* L1 window vertical position configuration */
|
|
|
|
0x94, /* L1 color keying configuration */
|
|
|
|
0x98, /* L1 pixel format configuration */
|
|
|
|
0x9c, /* L1 constant alpha configuration */
|
|
|
|
0xa0, /* L1 default color configuration */
|
|
|
|
0xa4, /* L1 blending factors configuration */
|
|
|
|
0xa8, /* L1 burst length configuration */
|
|
|
|
0x00, /* not available */
|
|
|
|
0xac, /* L1 color frame buffer address */
|
|
|
|
0xb0, /* L1 color frame buffer length */
|
|
|
|
0xb4, /* L1 color frame buffer line number */
|
|
|
|
0xb8, /* L1 auxiliary frame buffer address 0 */
|
|
|
|
0xbc, /* L1 auxiliary frame buffer address 1 */
|
|
|
|
0xc0, /* L1 auxiliary frame buffer length */
|
|
|
|
0xc4, /* L1 auxiliary frame buffer line number */
|
|
|
|
0xc8, /* L1 CLUT write */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00, /* not available */
|
|
|
|
0x00 /* not available */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const u32 ltdc_layer_regs_a2[] = {
|
|
|
|
0x100, /* L1 configuration 0 */
|
|
|
|
0x104, /* L1 configuration 1 */
|
|
|
|
0x108, /* L1 reload control */
|
|
|
|
0x10c, /* L1 control register */
|
|
|
|
0x110, /* L1 window horizontal position configuration */
|
|
|
|
0x114, /* L1 window vertical position configuration */
|
|
|
|
0x118, /* L1 color keying configuration */
|
|
|
|
0x11c, /* L1 pixel format configuration */
|
|
|
|
0x120, /* L1 constant alpha configuration */
|
|
|
|
0x124, /* L1 default color configuration */
|
|
|
|
0x128, /* L1 blending factors configuration */
|
|
|
|
0x12c, /* L1 burst length configuration */
|
|
|
|
0x130, /* L1 planar configuration */
|
|
|
|
0x134, /* L1 color frame buffer address */
|
|
|
|
0x138, /* L1 color frame buffer length */
|
|
|
|
0x13c, /* L1 color frame buffer line number */
|
|
|
|
0x140, /* L1 auxiliary frame buffer address 0 */
|
|
|
|
0x144, /* L1 auxiliary frame buffer address 1 */
|
|
|
|
0x148, /* L1 auxiliary frame buffer length */
|
|
|
|
0x14c, /* L1 auxiliary frame buffer line number */
|
|
|
|
0x150, /* L1 CLUT write */
|
|
|
|
0x16c, /* L1 Conversion YCbCr RGB 0 */
|
|
|
|
0x170, /* L1 Conversion YCbCr RGB 1 */
|
|
|
|
0x174, /* L1 Flexible Pixel Format 0 */
|
|
|
|
0x178 /* L1 Flexible Pixel Format 1 */
|
|
|
|
};
|
|
|
|
|
2019-04-03 17:25:14 +08:00
|
|
|
static const u64 ltdc_format_modifiers[] = {
|
|
|
|
DRM_FORMAT_MOD_LINEAR,
|
|
|
|
DRM_FORMAT_MOD_INVALID
|
|
|
|
};
|
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
static const struct regmap_config stm32_ltdc_regmap_cfg = {
|
|
|
|
.reg_bits = 32,
|
|
|
|
.val_bits = 32,
|
|
|
|
.reg_stride = sizeof(u32),
|
|
|
|
.max_register = 0x400,
|
|
|
|
.use_relaxed_mmio = true,
|
|
|
|
.cache_type = REGCACHE_NONE,
|
|
|
|
};
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
static const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
|
|
|
|
[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
|
|
|
|
0x02040199, /* (b_cb = 516 / r_cr = 409) */
|
|
|
|
0x006400D0 /* (g_cb = 100 / g_cr = 208) */
|
|
|
|
},
|
|
|
|
[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
|
|
|
|
0x01C60167, /* (b_cb = 454 / r_cr = 359) */
|
|
|
|
0x005800B7 /* (g_cb = 88 / g_cr = 183) */
|
|
|
|
},
|
|
|
|
[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
|
|
|
|
0x021D01CB, /* (b_cb = 541 / r_cr = 459) */
|
|
|
|
0x00370089 /* (g_cb = 55 / g_cr = 137) */
|
|
|
|
},
|
|
|
|
[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
|
|
|
|
0x01DB0193, /* (b_cb = 475 / r_cr = 403) */
|
|
|
|
0x00300078 /* (g_cb = 48 / g_cr = 120) */
|
|
|
|
}
|
|
|
|
/* BT2020 not supported */
|
|
|
|
};
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
return (struct ltdc_device *)crtc->dev->dev_private;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
|
|
|
|
{
|
|
|
|
return (struct ltdc_device *)plane->dev->dev_private;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
|
|
|
|
{
|
|
|
|
return (struct ltdc_device *)enc->dev->dev_private;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
|
|
|
|
{
|
|
|
|
enum ltdc_pix_fmt pf;
|
|
|
|
|
|
|
|
switch (drm_fmt) {
|
|
|
|
case DRM_FORMAT_ARGB8888:
|
|
|
|
case DRM_FORMAT_XRGB8888:
|
|
|
|
pf = PF_ARGB8888;
|
|
|
|
break;
|
2021-12-16 05:48:35 +08:00
|
|
|
case DRM_FORMAT_ABGR8888:
|
|
|
|
case DRM_FORMAT_XBGR8888:
|
|
|
|
pf = PF_ABGR8888;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case DRM_FORMAT_RGBA8888:
|
|
|
|
case DRM_FORMAT_RGBX8888:
|
|
|
|
pf = PF_RGBA8888;
|
|
|
|
break;
|
2021-12-16 05:48:35 +08:00
|
|
|
case DRM_FORMAT_BGRA8888:
|
|
|
|
case DRM_FORMAT_BGRX8888:
|
|
|
|
pf = PF_BGRA8888;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case DRM_FORMAT_RGB888:
|
|
|
|
pf = PF_RGB888;
|
|
|
|
break;
|
2021-12-16 05:48:35 +08:00
|
|
|
case DRM_FORMAT_BGR888:
|
|
|
|
pf = PF_BGR888;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case DRM_FORMAT_RGB565:
|
|
|
|
pf = PF_RGB565;
|
|
|
|
break;
|
2021-12-16 05:48:35 +08:00
|
|
|
case DRM_FORMAT_BGR565:
|
|
|
|
pf = PF_BGR565;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case DRM_FORMAT_ARGB1555:
|
|
|
|
case DRM_FORMAT_XRGB1555:
|
|
|
|
pf = PF_ARGB1555;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_ARGB4444:
|
|
|
|
case DRM_FORMAT_XRGB4444:
|
|
|
|
pf = PF_ARGB4444;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_C8:
|
|
|
|
pf = PF_L8;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pf = PF_NONE;
|
|
|
|
break;
|
2017-07-20 20:05:53 +08:00
|
|
|
/* Note: There are no DRM_FORMAT for AL44 and AL88 */
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return pf;
|
|
|
|
}
|
|
|
|
|
2021-12-16 05:48:35 +08:00
|
|
|
static inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
2021-12-16 05:48:35 +08:00
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
|
|
|
u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
|
|
|
|
int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
|
|
|
|
|
|
|
|
switch (pix_fmt) {
|
|
|
|
case PF_BGR888:
|
|
|
|
psize = 3;
|
|
|
|
alen = 0; apos = 0; rlen = 8; rpos = 0;
|
|
|
|
glen = 8; gpos = 8; blen = 8; bpos = 16;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case PF_ARGB1555:
|
2021-12-16 05:48:35 +08:00
|
|
|
psize = 2;
|
|
|
|
alen = 1; apos = 15; rlen = 5; rpos = 10;
|
|
|
|
glen = 5; gpos = 5; blen = 5; bpos = 0;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case PF_ARGB4444:
|
2021-12-16 05:48:35 +08:00
|
|
|
psize = 2;
|
|
|
|
alen = 4; apos = 12; rlen = 4; rpos = 8;
|
|
|
|
glen = 4; gpos = 4; blen = 4; bpos = 0;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
case PF_L8:
|
2021-12-16 05:48:35 +08:00
|
|
|
psize = 1;
|
|
|
|
alen = 0; apos = 0; rlen = 8; rpos = 0;
|
|
|
|
glen = 8; gpos = 0; blen = 8; bpos = 0;
|
|
|
|
break;
|
|
|
|
case PF_AL44:
|
|
|
|
psize = 1;
|
|
|
|
alen = 4; apos = 4; rlen = 4; rpos = 0;
|
|
|
|
glen = 4; gpos = 0; blen = 4; bpos = 0;
|
|
|
|
break;
|
|
|
|
case PF_AL88:
|
|
|
|
psize = 2;
|
|
|
|
alen = 8; apos = 8; rlen = 8; rpos = 0;
|
|
|
|
glen = 8; gpos = 0; blen = 8; bpos = 0;
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
default:
|
2021-12-16 05:48:35 +08:00
|
|
|
ret = NB_PF; /* error case, trace msg is handled by the caller */
|
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
2021-12-16 05:48:35 +08:00
|
|
|
|
|
|
|
if (ret == PF_FLEXIBLE) {
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
|
|
|
|
(rlen << 14) + (rpos << 9) + (alen << 5) + apos);
|
|
|
|
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
|
|
|
|
(psize << 18) + (blen << 14) + (bpos << 9) + (glen << 5) + gpos);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2021-12-16 05:48:35 +08:00
|
|
|
/*
|
|
|
|
* All non-alpha color formats derived from native alpha color formats are
|
|
|
|
* either characterized by a FourCC format code
|
|
|
|
*/
|
|
|
|
static inline u32 is_xrgb(u32 drm)
|
2018-02-01 18:42:42 +08:00
|
|
|
{
|
2021-12-16 05:48:35 +08:00
|
|
|
return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
|
2018-02-01 18:42:42 +08:00
|
|
|
}
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
|
|
|
struct drm_plane_state *state = plane->state;
|
|
|
|
u32 lofs = plane->index * LAY_OFS;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
switch (drm_pix_fmt) {
|
|
|
|
case DRM_FORMAT_YUYV:
|
|
|
|
val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_YVYU:
|
|
|
|
val = (YCM_I << 4) | LxPCR_YF;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_UYVY:
|
|
|
|
val = (YCM_I << 4) | LxPCR_CBF;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_VYUY:
|
|
|
|
val = (YCM_I << 4);
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_NV12:
|
|
|
|
val = (YCM_SP << 4) | LxPCR_CBF;
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_NV21:
|
|
|
|
val = (YCM_SP << 4);
|
|
|
|
break;
|
|
|
|
case DRM_FORMAT_YUV420:
|
|
|
|
case DRM_FORMAT_YVU420:
|
|
|
|
val = (YCM_FP << 4);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* RGB or not a YCbCr supported format */
|
2022-02-22 23:20:46 +08:00
|
|
|
DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
|
|
|
|
return;
|
2021-12-16 05:48:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable limited range */
|
|
|
|
if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
|
|
|
|
val |= LxPCR_YREN;
|
|
|
|
|
|
|
|
/* enable ycbcr conversion */
|
|
|
|
val |= LxPCR_YCEN;
|
|
|
|
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
|
|
|
struct drm_plane_state *state = plane->state;
|
|
|
|
enum drm_color_encoding enc = state->color_encoding;
|
|
|
|
enum drm_color_range ran = state->color_range;
|
|
|
|
u32 lofs = plane->index * LAY_OFS;
|
|
|
|
|
|
|
|
if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
|
|
|
|
DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
|
|
|
|
/* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
|
|
|
|
enc = DRM_COLOR_YCBCR_BT601;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
|
|
|
|
DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
|
|
|
|
/* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
|
|
|
|
ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
|
|
|
|
ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
|
|
|
|
ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
|
|
|
|
}
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
static inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
|
|
|
|
struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
u32 crc;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
|
|
|
|
ldev->crc_skip_count++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the CRC of the frame */
|
|
|
|
ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
|
|
|
|
if (ret)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Report to DRM the CRC (hw dependent feature) */
|
|
|
|
drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
|
|
|
|
{
|
|
|
|
struct drm_device *ddev = arg;
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
|
|
|
|
|
|
|
|
/* Line IRQ : trigger the vblank event */
|
2022-02-11 18:46:20 +08:00
|
|
|
if (ldev->irq_status & ISR_LIF) {
|
2017-04-14 18:13:34 +08:00
|
|
|
drm_crtc_handle_vblank(crtc);
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
/* Early return if CRC is not active */
|
|
|
|
if (ldev->crc_active)
|
|
|
|
ltdc_irq_crc_handle(ldev, crtc);
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
mutex_lock(&ldev->err_lock);
|
|
|
|
if (ldev->irq_status & ISR_TERRIF)
|
2022-06-03 21:46:54 +08:00
|
|
|
ldev->transfer_err++;
|
|
|
|
if (ldev->irq_status & ISR_FUEIF)
|
|
|
|
ldev->fifo_err++;
|
|
|
|
if (ldev->irq_status & ISR_FUWIF)
|
|
|
|
ldev->fifo_warn++;
|
2017-04-14 18:13:34 +08:00
|
|
|
mutex_unlock(&ldev->err_lock);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t ltdc_irq(int irq, void *arg)
|
|
|
|
{
|
|
|
|
struct drm_device *ddev = arg;
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
/*
|
|
|
|
* Read & Clear the interrupt status
|
|
|
|
* In order to write / read registers in this critical section
|
|
|
|
* very quickly, the regmap functions are not used.
|
|
|
|
*/
|
|
|
|
ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
|
|
|
|
writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
return IRQ_WAKE_THREAD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* DRM_CRTC
|
|
|
|
*/
|
|
|
|
|
2017-10-26 19:17:46 +08:00
|
|
|
static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
struct drm_color_lut *lut;
|
|
|
|
u32 val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
|
|
|
|
|
|
|
|
for (i = 0; i < CLUT_SIZE; i++, lut++) {
|
|
|
|
val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
|
|
|
|
(lut->blue >> 8) | (i << 24);
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
|
2017-10-26 19:17:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-30 17:36:44 +08:00
|
|
|
static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
2020-03-01 06:16:49 +08:00
|
|
|
struct drm_device *ddev = crtc->dev;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
2020-03-01 06:16:49 +08:00
|
|
|
pm_runtime_get_sync(ddev->dev);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
/* Sets the background color value */
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Enable IRQ */
|
2022-06-03 21:46:54 +08:00
|
|
|
regmap_set_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2019-04-01 15:24:17 +08:00
|
|
|
/* Commit shadow registers = update planes at next vblank */
|
2021-12-16 05:48:17 +08:00
|
|
|
if (!ldev->caps.plane_reg_shadow)
|
|
|
|
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
drm_crtc_vblank_on(crtc);
|
|
|
|
}
|
|
|
|
|
2017-06-30 17:36:45 +08:00
|
|
|
static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.
In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_disable(crtc, crtc_state);
+ FUNCS->atomic_disable(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@
drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
<...
- FUNCS->atomic_enable(crtc, crtc_state);
+ FUNCS->atomic_enable(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_enable = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-08 20:44:08 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
2019-06-03 16:32:02 +08:00
|
|
|
struct drm_device *ddev = crtc->dev;
|
2022-06-03 21:41:51 +08:00
|
|
|
int layer_index = 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
drm_crtc_vblank_off(crtc);
|
|
|
|
|
2022-06-03 21:41:51 +08:00
|
|
|
/* Disable all layers */
|
|
|
|
for (layer_index = 0; layer_index < ldev->caps.nb_layers; layer_index++)
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CR + layer_index * LAY_OFS,
|
|
|
|
LXCR_CLUTEN | LXCR_LEN, 0);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
/* disable IRQ */
|
2022-06-03 21:46:54 +08:00
|
|
|
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_FUWIE | IER_FUEIE | IER_RRIE | IER_TERRIE);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* immediately commit disable of layers before switching off LTDC */
|
2021-12-16 05:48:17 +08:00
|
|
|
if (!ldev->caps.plane_reg_shadow)
|
|
|
|
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
|
2019-06-03 16:32:02 +08:00
|
|
|
|
|
|
|
pm_runtime_put_sync(ddev->dev);
|
2022-06-03 21:46:54 +08:00
|
|
|
|
|
|
|
/* clear interrupt error counters */
|
|
|
|
mutex_lock(&ldev->err_lock);
|
|
|
|
ldev->transfer_err = 0;
|
|
|
|
ldev->fifo_err = 0;
|
|
|
|
ldev->fifo_warn = 0;
|
|
|
|
mutex_unlock(&ldev->err_lock);
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2018-04-17 19:40:26 +08:00
|
|
|
#define CLK_TOLERANCE_HZ 50
|
|
|
|
|
|
|
|
static enum drm_mode_status
|
|
|
|
ltdc_crtc_mode_valid(struct drm_crtc *crtc,
|
|
|
|
const struct drm_display_mode *mode)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
int target = mode->clock * 1000;
|
|
|
|
int target_min = target - CLK_TOLERANCE_HZ;
|
|
|
|
int target_max = target + CLK_TOLERANCE_HZ;
|
|
|
|
int result;
|
|
|
|
|
2018-06-29 19:22:22 +08:00
|
|
|
result = clk_round_rate(ldev->pixel_clk, target);
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result);
|
|
|
|
|
|
|
|
/* Filter modes according to the max frequency supported by the pads */
|
|
|
|
if (result > ldev->caps.pad_max_freq_hz)
|
|
|
|
return MODE_CLOCK_HIGH;
|
|
|
|
|
2018-04-17 19:40:26 +08:00
|
|
|
/*
|
|
|
|
* Accept all "preferred" modes:
|
|
|
|
* - this is important for panels because panel clock tolerances are
|
|
|
|
* bigger than hdmi ones and there is no reason to not accept them
|
|
|
|
* (the fps may vary a little but it is not a problem).
|
|
|
|
* - the hdmi preferred mode will be accepted too, but userland will
|
|
|
|
* be able to use others hdmi "valid" modes if necessary.
|
|
|
|
*/
|
|
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED)
|
|
|
|
return MODE_OK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Filter modes according to the clock value, particularly useful for
|
|
|
|
* hdmi modes that require precise pixel clocks.
|
|
|
|
*/
|
|
|
|
if (result < target_min || result > target_max)
|
|
|
|
return MODE_CLOCK_RANGE;
|
|
|
|
|
|
|
|
return MODE_OK;
|
|
|
|
}
|
|
|
|
|
2018-01-26 00:01:01 +08:00
|
|
|
static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
|
|
const struct drm_display_mode *mode,
|
|
|
|
struct drm_display_mode *adjusted_mode)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
int rate = mode->clock * 1000;
|
|
|
|
|
|
|
|
if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
|
|
|
|
DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
|
|
|
|
|
2019-05-10 23:03:20 +08:00
|
|
|
DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n",
|
|
|
|
mode->clock, adjusted_mode->clock);
|
|
|
|
|
2018-01-26 00:01:01 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
2019-06-03 16:32:02 +08:00
|
|
|
struct drm_device *ddev = crtc->dev;
|
2021-01-27 19:07:56 +08:00
|
|
|
struct drm_connector_list_iter iter;
|
|
|
|
struct drm_connector *connector = NULL;
|
2022-03-27 13:53:55 +08:00
|
|
|
struct drm_encoder *encoder = NULL, *en_iter;
|
|
|
|
struct drm_bridge *bridge = NULL, *br_iter;
|
2017-04-14 18:13:34 +08:00
|
|
|
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
|
|
|
u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
|
|
|
|
u32 total_width, total_height;
|
2021-12-16 05:47:50 +08:00
|
|
|
u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
|
2021-01-27 19:07:56 +08:00
|
|
|
u32 bus_flags = 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
u32 val;
|
2019-06-03 16:32:02 +08:00
|
|
|
int ret;
|
|
|
|
|
2021-01-27 19:07:56 +08:00
|
|
|
/* get encoder from crtc */
|
2022-03-27 13:53:55 +08:00
|
|
|
drm_for_each_encoder(en_iter, ddev)
|
|
|
|
if (en_iter->crtc == crtc) {
|
|
|
|
encoder = en_iter;
|
2021-01-27 19:07:56 +08:00
|
|
|
break;
|
2022-03-27 13:53:55 +08:00
|
|
|
}
|
2021-01-27 19:07:56 +08:00
|
|
|
|
|
|
|
if (encoder) {
|
|
|
|
/* get bridge from encoder */
|
2022-03-27 13:53:55 +08:00
|
|
|
list_for_each_entry(br_iter, &encoder->bridge_chain, chain_node)
|
|
|
|
if (br_iter->encoder == encoder) {
|
|
|
|
bridge = br_iter;
|
2021-01-27 19:07:56 +08:00
|
|
|
break;
|
2022-03-27 13:53:55 +08:00
|
|
|
}
|
2021-01-27 19:07:56 +08:00
|
|
|
|
|
|
|
/* Get the connector from encoder */
|
|
|
|
drm_connector_list_iter_begin(ddev, &iter);
|
|
|
|
drm_for_each_connector_iter(connector, &iter)
|
|
|
|
if (connector->encoder == encoder)
|
|
|
|
break;
|
|
|
|
drm_connector_list_iter_end(&iter);
|
|
|
|
}
|
|
|
|
|
2022-06-03 21:43:34 +08:00
|
|
|
if (bridge && bridge->timings) {
|
2021-01-27 19:07:56 +08:00
|
|
|
bus_flags = bridge->timings->input_bus_flags;
|
2022-06-03 21:43:34 +08:00
|
|
|
} else if (connector) {
|
2021-01-27 19:07:56 +08:00
|
|
|
bus_flags = connector->display_info.bus_flags;
|
2021-12-16 05:47:50 +08:00
|
|
|
if (connector->display_info.num_bus_formats)
|
|
|
|
bus_formats = connector->display_info.bus_formats[0];
|
|
|
|
}
|
2021-01-27 19:07:56 +08:00
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
if (!pm_runtime_active(ddev->dev)) {
|
|
|
|
ret = pm_runtime_get_sync(ddev->dev);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Failed to set mode, cannot get sync\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
|
2021-06-08 01:24:57 +08:00
|
|
|
DRM_DEBUG_DRIVER("Video mode: %dx%d", mode->hdisplay, mode->vdisplay);
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
|
2021-06-08 01:24:57 +08:00
|
|
|
mode->hsync_start - mode->hdisplay,
|
|
|
|
mode->htotal - mode->hsync_end,
|
|
|
|
mode->hsync_end - mode->hsync_start,
|
|
|
|
mode->vsync_start - mode->vdisplay,
|
|
|
|
mode->vtotal - mode->vsync_end,
|
|
|
|
mode->vsync_end - mode->vsync_start);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Convert video timings to ltdc timings */
|
2021-06-08 01:24:57 +08:00
|
|
|
hsync = mode->hsync_end - mode->hsync_start - 1;
|
|
|
|
vsync = mode->vsync_end - mode->vsync_start - 1;
|
|
|
|
accum_hbp = mode->htotal - mode->hsync_start - 1;
|
|
|
|
accum_vbp = mode->vtotal - mode->vsync_start - 1;
|
|
|
|
accum_act_w = accum_hbp + mode->hdisplay;
|
|
|
|
accum_act_h = accum_vbp + mode->vdisplay;
|
|
|
|
total_width = mode->htotal - 1;
|
|
|
|
total_height = mode->vtotal - 1;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2017-07-20 20:05:52 +08:00
|
|
|
/* Configures the HS, VS, DE and PC polarities. Default Active Low */
|
|
|
|
val = 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-06-08 01:24:57 +08:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
|
2017-07-20 20:05:52 +08:00
|
|
|
val |= GCR_HSPOL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-06-08 01:24:57 +08:00
|
|
|
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
2017-07-20 20:05:52 +08:00
|
|
|
val |= GCR_VSPOL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-01-27 19:07:56 +08:00
|
|
|
if (bus_flags & DRM_BUS_FLAG_DE_LOW)
|
2017-07-20 20:05:52 +08:00
|
|
|
val |= GCR_DEPOL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-01-27 19:07:56 +08:00
|
|
|
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
|
2017-07-20 20:05:52 +08:00
|
|
|
val |= GCR_PCPOL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_update_bits(ldev->regmap, LTDC_GCR,
|
|
|
|
GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Set Synchronization size */
|
|
|
|
val = (hsync << 16) | vsync;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Set Accumulated Back porch */
|
|
|
|
val = (accum_hbp << 16) | accum_vbp;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Set Accumulated Active Width */
|
|
|
|
val = (accum_act_w << 16) | accum_act_h;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Set total width & height */
|
|
|
|
val = (total_width << 16) | total_height;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
|
2021-12-16 05:47:50 +08:00
|
|
|
|
|
|
|
/* Configure the output format (hw version dependent) */
|
|
|
|
if (ldev->caps.ycbcr_output) {
|
|
|
|
/* Input video dynamic_range & colorimetry */
|
|
|
|
int vic = drm_match_cea_mode(mode);
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
|
|
|
|
vic == 2 || vic == 3 || vic == 17 || vic == 18)
|
|
|
|
/* ITU-R BT.601 */
|
|
|
|
val = 0;
|
|
|
|
else
|
|
|
|
/* ITU-R BT.709 */
|
|
|
|
val = EDCR_OCYSEL;
|
|
|
|
|
|
|
|
switch (bus_formats) {
|
|
|
|
case MEDIA_BUS_FMT_YUYV8_1X16:
|
|
|
|
/* enable ycbcr output converter */
|
|
|
|
regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
|
|
|
|
break;
|
|
|
|
case MEDIA_BUS_FMT_YVYU8_1X16:
|
|
|
|
/* enable ycbcr output converter & invert chrominance order */
|
|
|
|
regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* disable ycbcr output converter */
|
|
|
|
regmap_write(ldev->regmap, LTDC_EDCR, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
|
drm/atomic: Pass the full state to CRTC atomic begin and flush
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's start convert all the remaining helpers to provide a consistent
interface, starting with the CRTC's atomic_begin and atomic_flush.
The conversion was done using the coccinelle script below, built tested on
all the drivers and actually tested on vc4.
virtual report
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_begin(crtc, old_crtc_state);
+ FUNCS->atomic_begin(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
identifier old_crtc_state, old_state;
identifier crtc;
identifier f;
@@
f(struct drm_crtc_state *old_crtc_state)
{
...
struct drm_atomic_state *old_state = old_crtc_state->state;
<...
- FUNCS->atomic_flush(crtc, old_crtc_state);
+ FUNCS->atomic_flush(crtc, old_state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_begin(crtc, crtc_state);
+ FUNCS->atomic_begin(crtc, state);
...>
}
@@
struct drm_crtc_helper_funcs *FUNCS;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
identifier dev, state;
identifier f;
@@
f(struct drm_device *dev, struct drm_atomic_state *state, ...)
{
<...
- FUNCS->atomic_flush(crtc, crtc_state);
+ FUNCS->atomic_flush(crtc, state);
...>
}
@@
identifier crtc, old_state;
@@
struct drm_crtc_helper_funcs {
...
- void (*atomic_begin)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_begin)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
- void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+ void (*atomic_flush)(struct drm_crtc *crtc, struct drm_atomic_state *state);
...
}
@ crtc_atomic_func @
identifier helpers;
identifier func;
@@
(
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_begin = func,
...,
};
|
static struct drm_crtc_helper_funcs helpers = {
...,
.atomic_flush = func,
...,
};
)
@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@
void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@
void func(...)
{
...
- T state = E;
+ T crtc_state = E;
<+...
- state
+ crtc_state
...+>
}
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@
void func(...)
{
...
- T state;
+ T crtc_state;
<+...
- state
+ crtc_state
...+>
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
+ struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
...
}
@@
identifier old_state;
identifier crtc;
@@
void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{
...
}
@@
identifier old_state;
identifier crtc;
@@
void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
);
@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@
void func(struct drm_crtc *crtc,
- struct drm_crtc_state *old_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20201028123222.1732139-2-maxime@cerno.tech
2020-10-28 20:32:22 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
2019-06-03 16:32:02 +08:00
|
|
|
struct drm_device *ddev = crtc->dev;
|
2017-04-14 18:13:34 +08:00
|
|
|
struct drm_pending_vblank_event *event = crtc->state->event;
|
|
|
|
|
|
|
|
DRM_DEBUG_ATOMIC("\n");
|
|
|
|
|
2017-10-26 19:17:46 +08:00
|
|
|
ltdc_crtc_update_clut(crtc);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
/* Commit shadow registers = update planes at next vblank */
|
2021-12-16 05:48:17 +08:00
|
|
|
if (!ldev->caps.plane_reg_shadow)
|
|
|
|
regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
if (event) {
|
|
|
|
crtc->state->event = NULL;
|
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
spin_lock_irq(&ddev->event_lock);
|
2017-04-14 18:13:34 +08:00
|
|
|
if (drm_crtc_vblank_get(crtc) == 0)
|
|
|
|
drm_crtc_arm_vblank_event(crtc, event);
|
|
|
|
else
|
|
|
|
drm_crtc_send_vblank_event(crtc, event);
|
2019-06-03 16:32:02 +08:00
|
|
|
spin_unlock_irq(&ddev->event_lock);
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-23 21:59:35 +08:00
|
|
|
static bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
|
|
|
|
bool in_vblank_irq,
|
|
|
|
int *vpos, int *hpos,
|
|
|
|
ktime_t *stime, ktime_t *etime,
|
|
|
|
const struct drm_display_mode *mode)
|
2018-06-29 21:01:40 +08:00
|
|
|
{
|
2020-01-23 21:59:35 +08:00
|
|
|
struct drm_device *ddev = crtc->dev;
|
2018-06-29 21:01:40 +08:00
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
int line, vactive_start, vactive_end, vtotal;
|
|
|
|
|
|
|
|
if (stime)
|
|
|
|
*stime = ktime_get();
|
|
|
|
|
|
|
|
/* The active area starts after vsync + front porch and ends
|
|
|
|
* at vsync + front porc + display size.
|
|
|
|
* The total height also include back porch.
|
|
|
|
* We have 3 possible cases to handle:
|
|
|
|
* - line < vactive_start: vpos = line - vactive_start and will be
|
|
|
|
* negative
|
|
|
|
* - vactive_start < line < vactive_end: vpos = line - vactive_start
|
|
|
|
* and will be positive
|
|
|
|
* - line > vactive_end: vpos = line - vtotal - vactive_start
|
|
|
|
* and will negative
|
|
|
|
*
|
|
|
|
* Computation for the two first cases are identical so we can
|
|
|
|
* simplify the code and only test if line > vactive_end
|
|
|
|
*/
|
2019-06-03 16:32:02 +08:00
|
|
|
if (pm_runtime_active(ddev->dev)) {
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_read(ldev->regmap, LTDC_CPSR, &line);
|
|
|
|
line &= CPSR_CYPOS;
|
|
|
|
regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
|
|
|
|
vactive_start &= BPCR_AVBP;
|
|
|
|
regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
|
|
|
|
vactive_end &= AWCR_AAH;
|
|
|
|
regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
|
|
|
|
vtotal &= TWCR_TOTALH;
|
2019-06-03 16:32:02 +08:00
|
|
|
|
|
|
|
if (line > vactive_end)
|
|
|
|
*vpos = line - vtotal - vactive_start;
|
|
|
|
else
|
|
|
|
*vpos = line - vactive_start;
|
|
|
|
} else {
|
|
|
|
*vpos = 0;
|
|
|
|
}
|
2018-06-29 21:01:40 +08:00
|
|
|
|
|
|
|
*hpos = 0;
|
|
|
|
|
|
|
|
if (etime)
|
|
|
|
*etime = ktime_get();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-23 21:59:35 +08:00
|
|
|
static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
|
|
|
|
.mode_valid = ltdc_crtc_mode_valid,
|
|
|
|
.mode_fixup = ltdc_crtc_mode_fixup,
|
|
|
|
.mode_set_nofb = ltdc_crtc_mode_set_nofb,
|
|
|
|
.atomic_flush = ltdc_crtc_atomic_flush,
|
|
|
|
.atomic_enable = ltdc_crtc_atomic_enable,
|
|
|
|
.atomic_disable = ltdc_crtc_atomic_disable,
|
|
|
|
.get_scanout_position = ltdc_crtc_get_scanout_position,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
struct drm_crtc_state *state = crtc->state;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
if (state->enable)
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
|
2020-01-23 21:59:35 +08:00
|
|
|
else
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
|
2020-01-23 21:59:35 +08:00
|
|
|
}
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
static int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
if (!crtc)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (source && strcmp(source, "auto") == 0) {
|
|
|
|
ldev->crc_active = true;
|
|
|
|
ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
|
|
|
|
} else if (!source) {
|
|
|
|
ldev->crc_active = false;
|
|
|
|
ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
|
|
|
|
} else {
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ldev->crc_skip_count = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
|
|
|
|
const char *source, size_t *values_cnt)
|
|
|
|
{
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
if (!crtc)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (source && strcmp(source, "auto") != 0) {
|
|
|
|
DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
|
|
|
|
source, crtc->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*values_cnt = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-03 21:46:54 +08:00
|
|
|
static void ltdc_crtc_atomic_print_state(struct drm_printer *p,
|
|
|
|
const struct drm_crtc_state *state)
|
|
|
|
{
|
|
|
|
struct drm_crtc *crtc = state->crtc;
|
|
|
|
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
|
|
|
|
|
|
|
|
drm_printf(p, "\ttransfer_error=%d\n", ldev->transfer_err);
|
|
|
|
drm_printf(p, "\tfifo_underrun_error=%d\n", ldev->fifo_err);
|
|
|
|
drm_printf(p, "\tfifo_underrun_warning=%d\n", ldev->fifo_warn);
|
|
|
|
drm_printf(p, "\tfifo_underrun_threshold=%d\n", ldev->fifo_threshold);
|
|
|
|
}
|
|
|
|
|
2017-07-20 20:05:54 +08:00
|
|
|
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
|
2017-04-14 18:13:34 +08:00
|
|
|
.destroy = drm_crtc_cleanup,
|
|
|
|
.set_config = drm_atomic_helper_set_config,
|
|
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
|
|
.reset = drm_atomic_helper_crtc_reset,
|
|
|
|
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
|
|
|
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
2018-04-08 05:29:37 +08:00
|
|
|
.enable_vblank = ltdc_crtc_enable_vblank,
|
|
|
|
.disable_vblank = ltdc_crtc_disable_vblank,
|
2020-01-23 21:59:36 +08:00
|
|
|
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
|
2022-06-03 21:46:54 +08:00
|
|
|
.atomic_print_state = ltdc_crtc_atomic_print_state,
|
2017-04-14 18:13:34 +08:00
|
|
|
};
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
|
|
|
|
.destroy = drm_crtc_cleanup,
|
|
|
|
.set_config = drm_atomic_helper_set_config,
|
|
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
|
|
.reset = drm_atomic_helper_crtc_reset,
|
|
|
|
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
|
|
|
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
|
|
|
.enable_vblank = ltdc_crtc_enable_vblank,
|
|
|
|
.disable_vblank = ltdc_crtc_disable_vblank,
|
|
|
|
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
|
|
|
|
.set_crc_source = ltdc_crtc_set_crc_source,
|
|
|
|
.verify_crc_source = ltdc_crtc_verify_crc_source,
|
2022-06-03 21:46:54 +08:00
|
|
|
.atomic_print_state = ltdc_crtc_atomic_print_state,
|
2022-02-11 18:46:20 +08:00
|
|
|
};
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
/*
|
|
|
|
* DRM_PLANE
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int ltdc_plane_atomic_check(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic_check
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert all the remaining helpers to provide a consistent
interface, starting with the planes atomic_check.
The conversion was done using the coccinelle script below plus some
manual changes for vmwgfx, built tested on all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
int (*atomic_check)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_check = func,
...,
};
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier dev;
identifier plane, plane_state, state;
@@
f(struct drm_device *dev, struct drm_atomic_state *state)
{
<+...
- FUNCS->atomic_check(plane, plane_state)
+ FUNCS->atomic_check(plane, state)
...+>
}
@ ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
... when != new_plane_state
}
@ adds_new_state depends on plane_atomic_func && !ignores_new_state @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *new_plane_state)
{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, new_plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *new_plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-4-maxime@cerno.tech
2021-02-19 20:00:24 +08:00
|
|
|
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
2021-02-19 20:00:22 +08:00
|
|
|
struct drm_framebuffer *fb = new_plane_state->fb;
|
2018-09-28 18:39:40 +08:00
|
|
|
u32 src_w, src_h;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
if (!fb)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* convert src_ from 16:16 format */
|
2021-02-19 20:00:22 +08:00
|
|
|
src_w = new_plane_state->src_w >> 16;
|
|
|
|
src_h = new_plane_state->src_h >> 16;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Reject scaling */
|
2021-02-19 20:00:22 +08:00
|
|
|
if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) {
|
2022-06-03 21:42:50 +08:00
|
|
|
DRM_DEBUG_DRIVER("Scaling is not supported");
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
drm: Use state helper instead of the plane state pointer
Many drivers reference the plane->state pointer in order to get the
current plane state in their atomic_update or atomic_disable hooks,
which would be the new plane state in the global atomic state since
_swap_state happened when those hooks are run.
Use the drm_atomic_get_new_plane_state helper to get that state to make it
more obvious.
This was made using the coccinelle script below:
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ adds_new_state @
identifier plane_atomic_func.func;
identifier plane, state;
identifier new_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state)
{
...
- struct drm_plane_state *new_state = plane->state;
+ struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
...
}
@ include depends on adds_new_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_new_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://lore.kernel.org/r/20210219120032.260676-1-maxime@cerno.tech
2021-02-19 20:00:30 +08:00
|
|
|
struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,
|
|
|
|
plane);
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
struct drm_framebuffer *fb = newstate->fb;
|
2017-04-14 18:13:34 +08:00
|
|
|
u32 lofs = plane->index * LAY_OFS;
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
u32 x0 = newstate->crtc_x;
|
|
|
|
u32 x1 = newstate->crtc_x + newstate->crtc_w - 1;
|
|
|
|
u32 y0 = newstate->crtc_y;
|
|
|
|
u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
|
2017-04-14 18:13:34 +08:00
|
|
|
u32 src_x, src_y, src_w, src_h;
|
2022-06-03 21:45:47 +08:00
|
|
|
u32 val, pitch_in_bytes, line_length, line_number, ahbp, avbp, bpcr;
|
|
|
|
u32 paddr, paddr1, paddr2;
|
2017-04-14 18:13:34 +08:00
|
|
|
enum ltdc_pix_fmt pf;
|
|
|
|
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
if (!newstate->crtc || !fb) {
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER("fb or crtc NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert src_ from 16:16 format */
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
src_x = newstate->src_x >> 16;
|
|
|
|
src_y = newstate->src_y >> 16;
|
|
|
|
src_w = newstate->src_w >> 16;
|
|
|
|
src_h = newstate->src_h >> 16;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2017-07-20 20:05:53 +08:00
|
|
|
DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
|
|
|
|
plane->base.id, fb->base.id,
|
|
|
|
src_w, src_h, src_x, src_y,
|
drm: Rename plane->state variables in atomic update and disable
Some drivers are storing the plane->state pointer in atomic_update and
atomic_disable in a variable simply called state, while the state passed
as an argument is called old_state.
In order to ease subsequent reworks and to avoid confusing or
inconsistent names, let's rename those variables to new_state.
This was done using the following coccinelle script, plus some manual
changes for mtk and tegra.
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
)
@ moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
symbol old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_state = plane->state;
...
}
@ depends on moves_new_state_old_state @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ new_state
...>
}
@ moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
symbol oldstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *oldstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *newstate = plane->state;
...
}
@ depends on moves_new_state_oldstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_state;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
<...
- state
+ newstate
...>
}
@ moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
symbol old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
...
- struct drm_plane_state *state = plane->state;
+ struct drm_plane_state *new_pstate = plane->state;
...
}
@ depends on moves_new_state_old_pstate @
identifier plane_atomic_func.func;
identifier plane;
identifier old_pstate;
symbol state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_pstate)
{
<...
- state
+ new_pstate
...>
}
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-8-maxime@cerno.tech
2021-02-19 20:00:28 +08:00
|
|
|
newstate->crtc_w, newstate->crtc_h,
|
|
|
|
newstate->crtc_x, newstate->crtc_y);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
ahbp = (bpcr & BPCR_AHBP) >> 16;
|
|
|
|
avbp = bpcr & BPCR_AVBP;
|
|
|
|
|
|
|
|
/* Configures the horizontal start and stop position */
|
|
|
|
val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
|
|
|
|
LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Configures the vertical start and stop position */
|
|
|
|
val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
|
|
|
|
LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Specifies the pixel format */
|
|
|
|
pf = to_ltdc_pixelformat(fb->format->format);
|
|
|
|
for (val = 0; val < NB_PF; val++)
|
|
|
|
if (ldev->caps.pix_fmt_hw[val] == pf)
|
|
|
|
break;
|
|
|
|
|
2021-12-16 05:48:35 +08:00
|
|
|
/* Use the flexible color format feature if necessary and available */
|
|
|
|
if (ldev->caps.pix_fmt_flex && val == NB_PF)
|
|
|
|
val = ltdc_set_flexible_pixel_format(plane, pf);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
if (val == NB_PF) {
|
|
|
|
DRM_ERROR("Pixel format %.4s not supported\n",
|
|
|
|
(char *)&fb->format->format);
|
2017-07-20 20:05:53 +08:00
|
|
|
val = 0; /* set by default ARGB 32 bits */
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Specifies the constant alpha value */
|
2021-09-07 23:15:34 +08:00
|
|
|
val = newstate->alpha >> 8;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Specifies the blending factors */
|
|
|
|
val = BF1_PAXCA | BF2_1PAXCA;
|
2018-02-01 18:42:42 +08:00
|
|
|
if (!fb->format->has_alpha)
|
|
|
|
val = BF1_CA | BF2_1CA;
|
|
|
|
|
2018-02-01 18:42:43 +08:00
|
|
|
/* Manage hw-specific capabilities */
|
|
|
|
if (ldev->caps.non_alpha_only_l1 &&
|
|
|
|
plane->type != DRM_PLANE_TYPE_PRIMARY)
|
|
|
|
val = BF1_PAXCA | BF2_1PAXCA;
|
|
|
|
|
2022-06-03 21:44:59 +08:00
|
|
|
if (ldev->caps.dynamic_zorder) {
|
|
|
|
val |= (newstate->normalized_zpos << 16);
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
|
|
|
|
LXBFCR_BF2 | LXBFCR_BF1 | LXBFCR_BOR, val);
|
|
|
|
} else {
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs,
|
|
|
|
LXBFCR_BF2 | LXBFCR_BF1, val);
|
|
|
|
}
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* Sets the FB address */
|
2022-08-02 08:04:02 +08:00
|
|
|
paddr = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 0);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_X)
|
|
|
|
paddr += (fb->format->cpp[0] * (x1 - x0 + 1)) - 1;
|
|
|
|
|
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y)
|
|
|
|
paddr += (fb->pitches[0] * (y1 - y0));
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
/* Configures the color frame buffer pitch in bytes & line length */
|
|
|
|
line_length = fb->format->cpp[0] *
|
|
|
|
(x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
|
|
|
|
|
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y)
|
|
|
|
/* Compute negative value (signed on 16 bits) for the picth */
|
|
|
|
pitch_in_bytes = 0x10000 - fb->pitches[0];
|
|
|
|
else
|
|
|
|
pitch_in_bytes = fb->pitches[0];
|
|
|
|
|
|
|
|
val = (pitch_in_bytes << 16) | line_length;
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
|
|
|
|
|
|
|
|
/* Configures the frame buffer line number */
|
|
|
|
line_number = y1 - y0 + 1;
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
if (ldev->caps.ycbcr_input) {
|
|
|
|
if (fb->format->is_yuv) {
|
|
|
|
switch (fb->format->format) {
|
|
|
|
case DRM_FORMAT_NV12:
|
|
|
|
case DRM_FORMAT_NV21:
|
2022-06-03 21:45:47 +08:00
|
|
|
/* Configure the auxiliary frame buffer address 0 */
|
2022-08-02 08:04:02 +08:00
|
|
|
paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
|
2022-06-03 21:45:47 +08:00
|
|
|
|
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_X)
|
|
|
|
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y)
|
|
|
|
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
|
2021-12-16 05:48:43 +08:00
|
|
|
break;
|
|
|
|
case DRM_FORMAT_YUV420:
|
2022-06-03 21:45:47 +08:00
|
|
|
/* Configure the auxiliary frame buffer address 0 & 1 */
|
2022-08-02 08:04:02 +08:00
|
|
|
paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
|
|
|
|
paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_X) {
|
|
|
|
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
|
|
|
|
paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
|
|
|
|
}
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y) {
|
|
|
|
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
|
|
|
|
paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
|
|
|
|
}
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
|
2021-12-16 05:48:43 +08:00
|
|
|
break;
|
|
|
|
case DRM_FORMAT_YVU420:
|
2022-06-03 21:45:47 +08:00
|
|
|
/* Configure the auxiliary frame buffer address 0 & 1 */
|
2022-08-02 08:04:02 +08:00
|
|
|
paddr1 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 2);
|
|
|
|
paddr2 = (u32)drm_fb_dma_get_gem_addr(fb, newstate, 1);
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_X) {
|
|
|
|
paddr1 += ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) - 1;
|
|
|
|
paddr2 += ((fb->format->cpp[2] * (x1 - x0 + 1)) >> 1) - 1;
|
|
|
|
}
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y) {
|
|
|
|
paddr1 += (fb->pitches[1] * (y1 - y0 - 1)) >> 1;
|
|
|
|
paddr2 += (fb->pitches[2] * (y1 - y0 - 1)) >> 1;
|
|
|
|
}
|
2021-12-16 05:48:43 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr1);
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr2);
|
2021-12-16 05:48:43 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
/*
|
|
|
|
* Set the length and the number of lines of the auxiliary
|
|
|
|
* buffers if the framebuffer contains more than one plane.
|
|
|
|
*/
|
|
|
|
if (fb->format->num_planes > 1) {
|
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_Y)
|
|
|
|
/*
|
|
|
|
* Compute negative value (signed on 16 bits)
|
|
|
|
* for the picth
|
|
|
|
*/
|
|
|
|
pitch_in_bytes = 0x10000 - fb->pitches[1];
|
|
|
|
else
|
|
|
|
pitch_in_bytes = fb->pitches[1];
|
|
|
|
|
|
|
|
line_length = ((fb->format->cpp[1] * (x1 - x0 + 1)) >> 1) +
|
|
|
|
(ldev->caps.bus_width >> 3) - 1;
|
|
|
|
|
|
|
|
/* Configure the auxiliary buffer length */
|
|
|
|
val = (pitch_in_bytes << 16) | line_length;
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
|
|
|
|
|
|
|
|
/* Configure the auxiliary frame buffer line number */
|
|
|
|
val = line_number >> 1;
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
|
|
|
|
}
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
/* Configure YCbC conversion coefficient */
|
|
|
|
ltdc_set_ycbcr_coeffs(plane);
|
|
|
|
|
|
|
|
/* Configure YCbCr format and enable/disable conversion */
|
|
|
|
ltdc_set_ycbcr_config(plane, fb->format->format);
|
|
|
|
} else {
|
|
|
|
/* disable ycbcr conversion */
|
|
|
|
regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
/* Enable layer and CLUT if needed */
|
|
|
|
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
|
|
|
|
val |= LXCR_LEN;
|
2022-06-03 21:45:47 +08:00
|
|
|
|
|
|
|
/* Enable horizontal mirroring if requested */
|
|
|
|
if (newstate->rotation & DRM_MODE_REFLECT_X)
|
|
|
|
val |= LXCR_HMEN;
|
|
|
|
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, val);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:17 +08:00
|
|
|
/* Commit shadow registers = update plane at next vblank */
|
|
|
|
if (ldev->caps.plane_reg_shadow)
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
|
|
|
|
LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
|
|
|
|
|
2018-04-08 05:35:03 +08:00
|
|
|
ldev->plane_fpsi[plane->index].counter++;
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
mutex_lock(&ldev->err_lock);
|
2022-06-03 21:46:54 +08:00
|
|
|
if (ldev->transfer_err) {
|
|
|
|
DRM_WARN("ltdc transfer error: %d\n", ldev->transfer_err);
|
|
|
|
ldev->transfer_err = 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
2022-06-03 21:46:54 +08:00
|
|
|
|
|
|
|
if (ldev->caps.fifo_threshold) {
|
|
|
|
if (ldev->fifo_err) {
|
|
|
|
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
|
|
|
|
ldev->fifo_err = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ldev->fifo_warn >= ldev->fifo_threshold) {
|
|
|
|
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
|
|
|
|
ldev->fifo_warn = 0;
|
|
|
|
}
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&ldev->err_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_plane_atomic_disable(struct drm_plane *plane,
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_atomic_state *state)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
drm/atomic: Pass the full state to planes atomic disable and update
The current atomic helpers have either their object state being passed as
an argument or the full atomic state.
The former is the pattern that was done at first, before switching to the
latter for new hooks or when it was needed.
Let's convert the remaining helpers to provide a consistent interface,
this time with the planes atomic_update and atomic_disable.
The conversion was done using the coccinelle script below, built tested on
all the drivers.
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_update)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@@
identifier plane, plane_state;
symbol state;
@@
struct drm_plane_helper_funcs {
...
void (*atomic_disable)(struct drm_plane *plane,
- struct drm_plane_state *plane_state);
+ struct drm_atomic_state *state);
...
}
@ plane_atomic_func @
identifier helpers;
identifier func;
@@
(
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_update = func,
...,
};
|
static const struct drm_plane_helper_funcs helpers = {
...,
.atomic_disable = func,
...,
};
)
@@
struct drm_plane_helper_funcs *FUNCS;
identifier f;
identifier crtc_state;
identifier plane, plane_state, state;
expression e;
@@
f(struct drm_crtc_state *crtc_state)
{
...
struct drm_atomic_state *state = e;
<+...
(
- FUNCS->atomic_disable(plane, plane_state)
+ FUNCS->atomic_disable(plane, state)
|
- FUNCS->atomic_update(plane, plane_state)
+ FUNCS->atomic_update(plane, state)
)
...+>
}
@@
identifier plane_atomic_func.func;
identifier plane;
symbol state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *state)
+ struct drm_plane_state *old_plane_state)
{
<...
- state
+ old_plane_state
...>
}
@ ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, old_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *old_state)
{
... when != old_state
}
@ adds_old_state depends on plane_atomic_func && !ignores_old_state @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane, struct drm_plane_state *plane_state)
{
+ struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
...
}
@ depends on plane_atomic_func @
identifier plane_atomic_func.func;
identifier plane, plane_state;
@@
func(struct drm_plane *plane,
- struct drm_plane_state *plane_state
+ struct drm_atomic_state *state
)
{ ... }
@ include depends on adds_old_state @
@@
#include <drm/drm_atomic.h>
@ no_include depends on !include && adds_old_state @
@@
+ #include <drm/drm_atomic.h>
#include <drm/...>
@@
identifier plane_atomic_func.func;
identifier plane, state;
identifier plane_state;
@@
func(struct drm_plane *plane, struct drm_atomic_state *state) {
...
struct drm_plane_state *plane_state = drm_atomic_get_old_plane_state(state, plane);
<+...
- plane_state->state
+ state
...+>
}
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210219120032.260676-9-maxime@cerno.tech
2021-02-19 20:00:29 +08:00
|
|
|
struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,
|
|
|
|
plane);
|
2017-04-14 18:13:34 +08:00
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
|
|
|
u32 lofs = plane->index * LAY_OFS;
|
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
/* Disable layer */
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN | LXCR_HMEN, 0);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:17 +08:00
|
|
|
/* Commit shadow registers = update plane at next vblank */
|
|
|
|
if (ldev->caps.plane_reg_shadow)
|
|
|
|
regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
|
|
|
|
LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
|
|
|
|
oldstate->crtc->base.id, plane->base.id);
|
|
|
|
}
|
|
|
|
|
2018-04-08 05:35:03 +08:00
|
|
|
static void ltdc_plane_atomic_print_state(struct drm_printer *p,
|
|
|
|
const struct drm_plane_state *state)
|
|
|
|
{
|
|
|
|
struct drm_plane *plane = state->plane;
|
|
|
|
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
|
|
|
struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
|
|
|
|
int ms_since_last;
|
|
|
|
ktime_t now;
|
|
|
|
|
|
|
|
now = ktime_get();
|
|
|
|
ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
|
|
|
|
|
|
|
|
drm_printf(p, "\tuser_updates=%dfps\n",
|
|
|
|
DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
|
|
|
|
|
|
|
|
fpsi->last_timestamp = now;
|
|
|
|
fpsi->counter = 0;
|
|
|
|
}
|
|
|
|
|
2017-07-20 20:05:54 +08:00
|
|
|
static const struct drm_plane_funcs ltdc_plane_funcs = {
|
2017-04-14 18:13:34 +08:00
|
|
|
.update_plane = drm_atomic_helper_update_plane,
|
|
|
|
.disable_plane = drm_atomic_helper_disable_plane,
|
|
|
|
.destroy = drm_plane_cleanup,
|
|
|
|
.reset = drm_atomic_helper_plane_reset,
|
|
|
|
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
|
|
|
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
2018-04-08 05:35:03 +08:00
|
|
|
.atomic_print_state = ltdc_plane_atomic_print_state,
|
2017-04-14 18:13:34 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
|
|
|
|
.atomic_check = ltdc_plane_atomic_check,
|
|
|
|
.atomic_update = ltdc_plane_atomic_update,
|
|
|
|
.atomic_disable = ltdc_plane_atomic_disable,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
2021-12-16 05:48:43 +08:00
|
|
|
enum drm_plane_type type,
|
|
|
|
int index)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
unsigned long possible_crtcs = CRTC_MASK;
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
struct device *dev = ddev->dev;
|
|
|
|
struct drm_plane *plane;
|
|
|
|
unsigned int i, nb_fmt = 0;
|
2021-12-16 05:48:35 +08:00
|
|
|
u32 *formats;
|
|
|
|
u32 drm_fmt;
|
2019-04-03 17:25:14 +08:00
|
|
|
const u64 *modifiers = ltdc_format_modifiers;
|
2021-12-16 05:48:43 +08:00
|
|
|
u32 lofs = index * LAY_OFS;
|
|
|
|
u32 val;
|
2017-04-14 18:13:34 +08:00
|
|
|
int ret;
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
/* Allocate the biggest size according to supported color formats */
|
|
|
|
formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb +
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) +
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) +
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) *
|
|
|
|
sizeof(*formats), GFP_KERNEL);
|
2018-02-01 18:42:42 +08:00
|
|
|
|
2021-12-16 05:48:35 +08:00
|
|
|
for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
|
|
|
|
drm_fmt = ldev->caps.pix_fmt_drm[i];
|
2018-02-01 18:42:43 +08:00
|
|
|
|
|
|
|
/* Manage hw-specific capabilities */
|
2021-12-16 05:48:35 +08:00
|
|
|
if (ldev->caps.non_alpha_only_l1)
|
|
|
|
/* XR24 & RX24 like formats supported only on primary layer */
|
|
|
|
if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
|
|
|
|
continue;
|
2018-02-01 18:42:43 +08:00
|
|
|
|
2021-12-16 05:48:35 +08:00
|
|
|
formats[nb_fmt++] = drm_fmt;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
/* Add YCbCr supported pixel formats */
|
|
|
|
if (ldev->caps.ycbcr_input) {
|
|
|
|
regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
|
|
|
|
if (val & LXCR_C1R_YIA) {
|
|
|
|
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
|
|
|
|
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
|
|
|
|
}
|
|
|
|
if (val & LXCR_C1R_YSPA) {
|
|
|
|
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
|
|
|
|
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
|
|
|
|
}
|
|
|
|
if (val & LXCR_C1R_YFPA) {
|
|
|
|
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
|
|
|
|
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
|
|
|
|
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
|
|
|
|
if (!plane)
|
2018-04-19 21:28:04 +08:00
|
|
|
return NULL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
|
|
|
|
<dc_plane_funcs, formats, nb_fmt,
|
2019-04-03 17:25:14 +08:00
|
|
|
modifiers, type, NULL);
|
2017-04-14 18:13:34 +08:00
|
|
|
if (ret < 0)
|
2018-04-19 21:28:04 +08:00
|
|
|
return NULL;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
if (ldev->caps.ycbcr_input) {
|
|
|
|
if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
|
|
|
|
drm_plane_create_color_properties(plane,
|
|
|
|
BIT(DRM_COLOR_YCBCR_BT601) |
|
|
|
|
BIT(DRM_COLOR_YCBCR_BT709),
|
|
|
|
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
|
|
|
|
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
|
|
|
|
DRM_COLOR_YCBCR_BT601,
|
|
|
|
DRM_COLOR_YCBCR_LIMITED_RANGE);
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
drm_plane_helper_add(plane, <dc_plane_helper_funcs);
|
|
|
|
|
2021-09-07 23:15:34 +08:00
|
|
|
drm_plane_create_alpha_property(plane);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
|
|
|
|
|
|
|
|
return plane;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_plane_destroy_all(struct drm_device *ddev)
|
|
|
|
{
|
|
|
|
struct drm_plane *plane, *plane_temp;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(plane, plane_temp,
|
|
|
|
&ddev->mode_config.plane_list, head)
|
|
|
|
drm_plane_cleanup(plane);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
struct drm_plane *primary, *overlay;
|
2022-06-03 21:45:47 +08:00
|
|
|
int supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y;
|
2017-04-14 18:13:34 +08:00
|
|
|
unsigned int i;
|
2017-07-20 20:05:56 +08:00
|
|
|
int ret;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-12-16 05:48:43 +08:00
|
|
|
primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
|
2017-04-14 18:13:34 +08:00
|
|
|
if (!primary) {
|
|
|
|
DRM_ERROR("Can not create primary plane\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-06-03 21:44:59 +08:00
|
|
|
if (ldev->caps.dynamic_zorder)
|
|
|
|
drm_plane_create_zpos_property(primary, 0, 0, ldev->caps.nb_layers - 1);
|
|
|
|
else
|
|
|
|
drm_plane_create_zpos_immutable_property(primary, 0);
|
2021-09-07 23:13:23 +08:00
|
|
|
|
2022-06-03 21:45:47 +08:00
|
|
|
if (ldev->caps.plane_rotation)
|
|
|
|
drm_plane_create_rotation_property(primary, DRM_MODE_ROTATE_0,
|
|
|
|
supported_rotations);
|
|
|
|
|
2022-02-11 18:46:20 +08:00
|
|
|
/* Init CRTC according to its hardware features */
|
|
|
|
if (ldev->caps.crc)
|
|
|
|
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
|
|
|
|
<dc_crtc_with_crc_support_funcs, NULL);
|
|
|
|
else
|
|
|
|
ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
|
|
|
|
<dc_crtc_funcs, NULL);
|
2017-07-20 20:05:56 +08:00
|
|
|
if (ret) {
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_ERROR("Can not initialize CRTC\n");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
drm_crtc_helper_add(crtc, <dc_crtc_helper_funcs);
|
|
|
|
|
2017-10-26 19:17:46 +08:00
|
|
|
drm_mode_crtc_set_gamma_size(crtc, CLUT_SIZE);
|
|
|
|
drm_crtc_enable_color_mgmt(crtc, 0, false, CLUT_SIZE);
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
|
|
|
|
|
|
|
|
/* Add planes. Note : the first layer is used by primary plane */
|
|
|
|
for (i = 1; i < ldev->caps.nb_layers; i++) {
|
2021-12-16 05:48:43 +08:00
|
|
|
overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
|
2017-04-14 18:13:34 +08:00
|
|
|
if (!overlay) {
|
2017-07-20 20:05:56 +08:00
|
|
|
ret = -ENOMEM;
|
2017-04-14 18:13:34 +08:00
|
|
|
DRM_ERROR("Can not create overlay plane %d\n", i);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2022-06-03 21:44:59 +08:00
|
|
|
if (ldev->caps.dynamic_zorder)
|
|
|
|
drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1);
|
|
|
|
else
|
|
|
|
drm_plane_create_zpos_immutable_property(overlay, i);
|
2022-06-03 21:45:47 +08:00
|
|
|
|
|
|
|
if (ldev->caps.plane_rotation)
|
|
|
|
drm_plane_create_rotation_property(overlay, DRM_MODE_ROTATE_0,
|
|
|
|
supported_rotations);
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
ltdc_plane_destroy_all(ddev);
|
2017-07-20 20:05:56 +08:00
|
|
|
return ret;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2019-09-06 17:21:48 +08:00
|
|
|
static void ltdc_encoder_disable(struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
struct drm_device *ddev = encoder->dev;
|
2019-11-27 18:23:38 +08:00
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
2019-09-06 17:21:48 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
2019-11-27 18:23:38 +08:00
|
|
|
/* Disable LTDC */
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
|
2019-11-27 18:23:38 +08:00
|
|
|
|
2019-09-06 17:21:48 +08:00
|
|
|
/* Set to sleep state the pinctrl whatever type of encoder */
|
|
|
|
pinctrl_pm_select_sleep_state(ddev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_encoder_enable(struct drm_encoder *encoder)
|
2019-11-27 18:23:38 +08:00
|
|
|
{
|
|
|
|
struct drm_device *ddev = encoder->dev;
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
2022-06-03 21:46:54 +08:00
|
|
|
/* set fifo underrun threshold register */
|
|
|
|
if (ldev->caps.fifo_threshold)
|
|
|
|
regmap_write(ldev->regmap, LTDC_FUT, ldev->fifo_threshold);
|
|
|
|
|
2019-11-27 18:23:38 +08:00
|
|
|
/* Enable LTDC */
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
|
2019-11-27 18:23:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ltdc_encoder_mode_set(struct drm_encoder *encoder,
|
|
|
|
struct drm_display_mode *mode,
|
|
|
|
struct drm_display_mode *adjusted_mode)
|
2019-09-06 17:21:48 +08:00
|
|
|
{
|
|
|
|
struct drm_device *ddev = encoder->dev;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set to default state the pinctrl only with DPI type.
|
|
|
|
* Others types like DSI, don't need pinctrl due to
|
|
|
|
* internal bridge (the signals do not come out of the chipset).
|
|
|
|
*/
|
|
|
|
if (encoder->encoder_type == DRM_MODE_ENCODER_DPI)
|
|
|
|
pinctrl_pm_select_default_state(ddev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_encoder_helper_funcs ltdc_encoder_helper_funcs = {
|
|
|
|
.disable = ltdc_encoder_disable,
|
|
|
|
.enable = ltdc_encoder_enable,
|
2019-11-27 18:23:38 +08:00
|
|
|
.mode_set = ltdc_encoder_mode_set,
|
2019-09-06 17:21:48 +08:00
|
|
|
};
|
|
|
|
|
2017-10-02 17:34:46 +08:00
|
|
|
static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
|
2017-04-14 18:13:34 +08:00
|
|
|
{
|
|
|
|
struct drm_encoder *encoder;
|
2017-07-17 15:40:18 +08:00
|
|
|
int ret;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
|
|
|
|
if (!encoder)
|
2017-07-17 15:40:18 +08:00
|
|
|
return -ENOMEM;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
encoder->possible_crtcs = CRTC_MASK;
|
2017-07-20 20:05:53 +08:00
|
|
|
encoder->possible_clones = 0; /* No cloning support */
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2021-03-03 01:57:00 +08:00
|
|
|
drm_simple_encoder_init(ddev, encoder, DRM_MODE_ENCODER_DPI);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2019-09-06 17:21:48 +08:00
|
|
|
drm_encoder_helper_add(encoder, <dc_encoder_helper_funcs);
|
|
|
|
|
drm/bridge: Extend bridge API to disable connector creation
Most bridge drivers create a DRM connector to model the connector at the
output of the bridge. This model is historical and has worked pretty
well so far, but causes several issues:
- It prevents supporting more complex display pipelines where DRM
connector operations are split over multiple components. For instance a
pipeline with a bridge connected to the DDC signals to read EDID data,
and another one connected to the HPD signal to detect connection and
disconnection, will not be possible to support through this model.
- It requires every bridge driver to implement similar connector
handling code, resulting in code duplication.
- It assumes that a bridge will either be wired to a connector or to
another bridge, but doesn't support bridges that can be used in both
positions very well (although there is some ad-hoc support for this in
the analogix_dp bridge driver).
In order to solve these issues, ownership of the connector should be
moved to the display controller driver (where it can be implemented
using helpers provided by the core).
Extend the bridge API to allow disabling connector creation in bridge
drivers as a first step towards the new model. The new flags argument to
the bridge .attach() operation allows instructing the bridge driver to
skip creating a connector. Unconditionally set the new flags argument to
0 for now to keep the existing behaviour, and modify all existing bridge
drivers to return an error when connector creation is not requested as
they don't support this feature yet.
The change is based on the following semantic patch, with manual review
and edits.
@ rule1 @
identifier funcs;
identifier fn;
@@
struct drm_bridge_funcs funcs = {
...,
.attach = fn
};
@ depends on rule1 @
identifier rule1.fn;
identifier bridge;
statement S, S1;
@@
int fn(
struct drm_bridge *bridge
+ , enum drm_bridge_attach_flags flags
)
{
... when != S
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
+ DRM_ERROR("Fix bridge driver to make connector optional!");
+ return -EINVAL;
+ }
+
S1
...
}
@ depends on rule1 @
identifier rule1.fn;
identifier bridge, flags;
expression E1, E2, E3;
@@
int fn(
struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags
) {
<...
drm_bridge_attach(E1, E2, E3
+ , flags
)
...>
}
@@
expression E1, E2, E3;
@@
drm_bridge_attach(E1, E2, E3
+ , 0
)
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-10-laurent.pinchart@ideasonboard.com
2020-02-26 19:24:29 +08:00
|
|
|
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
|
2017-07-17 15:40:18 +08:00
|
|
|
if (ret) {
|
2021-07-04 21:59:14 +08:00
|
|
|
if (ret != -EPROBE_DEFER)
|
|
|
|
drm_encoder_cleanup(encoder);
|
|
|
|
return ret;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2017-07-17 15:40:18 +08:00
|
|
|
DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2017-07-17 15:40:18 +08:00
|
|
|
return 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ltdc_get_caps(struct drm_device *ddev)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
u32 bus_width_log2, lcr, gc2r;
|
|
|
|
|
2019-04-03 17:20:16 +08:00
|
|
|
/*
|
|
|
|
* at least 1 layer must be managed & the number of layers
|
|
|
|
* must not exceed LTDC_MAX_LAYER
|
|
|
|
*/
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_read(ldev->regmap, LTDC_LCR, &lcr);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2019-04-03 17:20:16 +08:00
|
|
|
ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
/* set data bus width */
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
|
2017-04-14 18:13:34 +08:00
|
|
|
bus_width_log2 = (gc2r & GC2R_BW) >> 4;
|
|
|
|
ldev->caps.bus_width = 8 << bus_width_log2;
|
2021-12-16 05:47:38 +08:00
|
|
|
regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
switch (ldev->caps.hw_version) {
|
|
|
|
case HWVER_10200:
|
|
|
|
case HWVER_10300:
|
2021-12-03 16:56:18 +08:00
|
|
|
ldev->caps.layer_ofs = LAY_OFS_0;
|
|
|
|
ldev->caps.layer_regs = ltdc_layer_regs_a0;
|
2017-04-14 18:13:34 +08:00
|
|
|
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
|
2021-12-16 05:48:35 +08:00
|
|
|
ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
|
|
|
|
ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
|
|
|
|
ldev->caps.pix_fmt_flex = false;
|
2018-02-01 18:42:43 +08:00
|
|
|
/*
|
|
|
|
* Hw older versions support non-alpha color formats derived
|
|
|
|
* from native alpha color formats only on the primary layer.
|
|
|
|
* For instance, RG16 native format without alpha works fine
|
|
|
|
* on 2nd layer but XR24 (derived color format from AR24)
|
|
|
|
* does not work on 2nd layer.
|
|
|
|
*/
|
|
|
|
ldev->caps.non_alpha_only_l1 = true;
|
2018-06-29 19:22:22 +08:00
|
|
|
ldev->caps.pad_max_freq_hz = 90000000;
|
|
|
|
if (ldev->caps.hw_version == HWVER_10200)
|
|
|
|
ldev->caps.pad_max_freq_hz = 65000000;
|
2020-01-21 18:13:52 +08:00
|
|
|
ldev->caps.nb_irq = 2;
|
2021-12-16 05:48:43 +08:00
|
|
|
ldev->caps.ycbcr_input = false;
|
2021-12-16 05:47:50 +08:00
|
|
|
ldev->caps.ycbcr_output = false;
|
2021-12-16 05:48:17 +08:00
|
|
|
ldev->caps.plane_reg_shadow = false;
|
2022-02-11 18:46:20 +08:00
|
|
|
ldev->caps.crc = false;
|
2022-06-03 21:44:59 +08:00
|
|
|
ldev->caps.dynamic_zorder = false;
|
2022-06-03 21:45:47 +08:00
|
|
|
ldev->caps.plane_rotation = false;
|
2022-06-03 21:46:54 +08:00
|
|
|
ldev->caps.fifo_threshold = false;
|
2017-04-14 18:13:34 +08:00
|
|
|
break;
|
|
|
|
case HWVER_20101:
|
2021-12-03 16:56:18 +08:00
|
|
|
ldev->caps.layer_ofs = LAY_OFS_0;
|
|
|
|
ldev->caps.layer_regs = ltdc_layer_regs_a1;
|
2017-04-14 18:13:34 +08:00
|
|
|
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
|
2021-12-16 05:48:35 +08:00
|
|
|
ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
|
|
|
|
ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
|
|
|
|
ldev->caps.pix_fmt_flex = false;
|
2018-02-01 18:42:43 +08:00
|
|
|
ldev->caps.non_alpha_only_l1 = false;
|
2018-06-29 19:22:22 +08:00
|
|
|
ldev->caps.pad_max_freq_hz = 150000000;
|
2020-01-21 18:13:52 +08:00
|
|
|
ldev->caps.nb_irq = 4;
|
2021-12-16 05:48:43 +08:00
|
|
|
ldev->caps.ycbcr_input = false;
|
2021-12-16 05:47:50 +08:00
|
|
|
ldev->caps.ycbcr_output = false;
|
2021-12-16 05:48:17 +08:00
|
|
|
ldev->caps.plane_reg_shadow = false;
|
2022-02-11 18:46:20 +08:00
|
|
|
ldev->caps.crc = false;
|
2022-06-03 21:44:59 +08:00
|
|
|
ldev->caps.dynamic_zorder = false;
|
2022-06-03 21:45:47 +08:00
|
|
|
ldev->caps.plane_rotation = false;
|
2022-06-03 21:46:54 +08:00
|
|
|
ldev->caps.fifo_threshold = false;
|
2017-04-14 18:13:34 +08:00
|
|
|
break;
|
2021-12-03 16:56:18 +08:00
|
|
|
case HWVER_40100:
|
|
|
|
ldev->caps.layer_ofs = LAY_OFS_1;
|
|
|
|
ldev->caps.layer_regs = ltdc_layer_regs_a2;
|
|
|
|
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
|
2021-12-16 05:48:35 +08:00
|
|
|
ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
|
|
|
|
ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
|
|
|
|
ldev->caps.pix_fmt_flex = true;
|
2021-12-03 16:56:18 +08:00
|
|
|
ldev->caps.non_alpha_only_l1 = false;
|
|
|
|
ldev->caps.pad_max_freq_hz = 90000000;
|
|
|
|
ldev->caps.nb_irq = 2;
|
2021-12-16 05:48:43 +08:00
|
|
|
ldev->caps.ycbcr_input = true;
|
2021-12-16 05:47:50 +08:00
|
|
|
ldev->caps.ycbcr_output = true;
|
2021-12-16 05:48:17 +08:00
|
|
|
ldev->caps.plane_reg_shadow = true;
|
2022-02-11 18:46:20 +08:00
|
|
|
ldev->caps.crc = true;
|
2022-06-03 21:44:59 +08:00
|
|
|
ldev->caps.dynamic_zorder = true;
|
2022-06-03 21:45:47 +08:00
|
|
|
ldev->caps.plane_rotation = true;
|
2022-06-03 21:46:54 +08:00
|
|
|
ldev->caps.fifo_threshold = true;
|
2021-12-03 16:56:18 +08:00
|
|
|
break;
|
2017-04-14 18:13:34 +08:00
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-21 16:15:20 +08:00
|
|
|
void ltdc_suspend(struct drm_device *ddev)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
clk_disable_unprepare(ldev->pixel_clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ltdc_resume(struct drm_device *ddev)
|
|
|
|
{
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
|
|
|
ret = clk_prepare_enable(ldev->pixel_clk);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-14 18:13:34 +08:00
|
|
|
int ltdc_load(struct drm_device *ddev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(ddev->dev);
|
|
|
|
struct ltdc_device *ldev = ddev->dev_private;
|
|
|
|
struct device *dev = ddev->dev;
|
|
|
|
struct device_node *np = dev->of_node;
|
2020-02-28 16:07:38 +08:00
|
|
|
struct drm_bridge *bridge;
|
|
|
|
struct drm_panel *panel;
|
2017-04-14 18:13:34 +08:00
|
|
|
struct drm_crtc *crtc;
|
|
|
|
struct reset_control *rstc;
|
2017-07-20 20:05:55 +08:00
|
|
|
struct resource *res;
|
2020-02-28 16:07:38 +08:00
|
|
|
int irq, i, nb_endpoints;
|
|
|
|
int ret = -ENODEV;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
2020-02-28 16:07:38 +08:00
|
|
|
/* Get number of endpoints */
|
|
|
|
nb_endpoints = of_graph_get_endpoint_count(np);
|
|
|
|
if (!nb_endpoints)
|
|
|
|
return -ENODEV;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
ldev->pixel_clk = devm_clk_get(dev, "lcd");
|
|
|
|
if (IS_ERR(ldev->pixel_clk)) {
|
2019-04-24 22:03:21 +08:00
|
|
|
if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER)
|
|
|
|
DRM_ERROR("Unable to get lcd clock\n");
|
|
|
|
return PTR_ERR(ldev->pixel_clk);
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (clk_prepare_enable(ldev->pixel_clk)) {
|
|
|
|
DRM_ERROR("Unable to prepare pixel clock\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2020-02-28 16:07:38 +08:00
|
|
|
/* Get endpoints if any */
|
|
|
|
for (i = 0; i < nb_endpoints; i++) {
|
|
|
|
ret = drm_of_find_panel_or_bridge(np, 0, i, &panel, &bridge);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If at least one endpoint is -ENODEV, continue probing,
|
|
|
|
* else if at least one endpoint returned an error
|
|
|
|
* (ie -EPROBE_DEFER) then stop probing.
|
|
|
|
*/
|
|
|
|
if (ret == -ENODEV)
|
|
|
|
continue;
|
|
|
|
else if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (panel) {
|
|
|
|
bridge = drm_panel_bridge_add_typed(panel,
|
|
|
|
DRM_MODE_CONNECTOR_DPI);
|
|
|
|
if (IS_ERR(bridge)) {
|
|
|
|
DRM_ERROR("panel-bridge endpoint %d\n", i);
|
|
|
|
ret = PTR_ERR(bridge);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bridge) {
|
|
|
|
ret = ltdc_encoder_init(ddev, bridge);
|
|
|
|
if (ret) {
|
2021-07-04 21:59:14 +08:00
|
|
|
if (ret != -EPROBE_DEFER)
|
|
|
|
DRM_ERROR("init encoder endpoint %d\n", i);
|
2020-02-28 16:07:38 +08:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rstc = devm_reset_control_get_exclusive(dev, NULL);
|
|
|
|
|
|
|
|
mutex_init(&ldev->err_lock);
|
|
|
|
|
2019-04-03 17:24:13 +08:00
|
|
|
if (!IS_ERR(rstc)) {
|
|
|
|
reset_control_assert(rstc);
|
|
|
|
usleep_range(10, 20);
|
|
|
|
reset_control_deassert(rstc);
|
|
|
|
}
|
|
|
|
|
2017-07-20 20:05:55 +08:00
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
ldev->regs = devm_ioremap_resource(dev, res);
|
2017-04-14 18:13:34 +08:00
|
|
|
if (IS_ERR(ldev->regs)) {
|
|
|
|
DRM_ERROR("Unable to get ltdc registers\n");
|
2017-07-17 15:40:17 +08:00
|
|
|
ret = PTR_ERR(ldev->regs);
|
|
|
|
goto err;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
2021-12-16 05:47:38 +08:00
|
|
|
ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
|
|
|
|
if (IS_ERR(ldev->regmap)) {
|
|
|
|
DRM_ERROR("Unable to regmap ltdc registers\n");
|
|
|
|
ret = PTR_ERR(ldev->regmap);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2020-01-21 18:13:52 +08:00
|
|
|
ret = ltdc_get_caps(ddev);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
|
|
|
|
ldev->caps.hw_version);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2022-06-03 21:46:54 +08:00
|
|
|
/* Disable interrupts */
|
|
|
|
if (ldev->caps.fifo_threshold)
|
|
|
|
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
|
|
|
|
IER_TERRIE);
|
|
|
|
else
|
|
|
|
regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUWIE |
|
|
|
|
IER_TERRIE | IER_FUEIE);
|
|
|
|
|
2020-01-21 18:13:52 +08:00
|
|
|
DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version);
|
|
|
|
|
2022-06-03 21:46:54 +08:00
|
|
|
/* initialize default value for fifo underrun threshold & clear interrupt error counters */
|
|
|
|
ldev->transfer_err = 0;
|
|
|
|
ldev->fifo_err = 0;
|
|
|
|
ldev->fifo_warn = 0;
|
|
|
|
ldev->fifo_threshold = FUT_DFT;
|
|
|
|
|
2020-01-21 18:13:52 +08:00
|
|
|
for (i = 0; i < ldev->caps.nb_irq; i++) {
|
2017-04-14 18:13:34 +08:00
|
|
|
irq = platform_get_irq(pdev, i);
|
2020-01-21 18:13:52 +08:00
|
|
|
if (irq < 0) {
|
|
|
|
ret = irq;
|
2019-04-24 22:03:20 +08:00
|
|
|
goto err;
|
2020-01-21 18:13:52 +08:00
|
|
|
}
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
|
|
|
|
ltdc_irq_thread, IRQF_ONESHOT,
|
|
|
|
dev_name(dev), ddev);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Failed to register LTDC interrupt\n");
|
2017-07-17 15:40:17 +08:00
|
|
|
goto err;
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
|
|
|
|
if (!crtc) {
|
|
|
|
DRM_ERROR("Failed to allocate crtc\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ltdc_crtc_init(ddev, crtc);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Failed to init crtc\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = drm_vblank_init(ddev, NB_CRTC);
|
|
|
|
if (ret) {
|
|
|
|
DRM_ERROR("Failed calling drm_vblank_init()\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
clk_disable_unprepare(ldev->pixel_clk);
|
|
|
|
|
2019-09-06 17:21:48 +08:00
|
|
|
pinctrl_pm_select_sleep_state(ddev->dev);
|
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
pm_runtime_enable(ddev->dev);
|
2017-07-17 15:40:18 +08:00
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
return 0;
|
2017-04-14 18:13:34 +08:00
|
|
|
err:
|
2020-02-28 16:07:38 +08:00
|
|
|
for (i = 0; i < nb_endpoints; i++)
|
|
|
|
drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
clk_disable_unprepare(ldev->pixel_clk);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ltdc_unload(struct drm_device *ddev)
|
|
|
|
{
|
2020-02-28 16:07:38 +08:00
|
|
|
struct device *dev = ddev->dev;
|
|
|
|
int nb_endpoints, i;
|
2017-04-14 18:13:34 +08:00
|
|
|
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
|
2020-02-28 16:07:38 +08:00
|
|
|
nb_endpoints = of_graph_get_endpoint_count(dev->of_node);
|
|
|
|
|
|
|
|
for (i = 0; i < nb_endpoints; i++)
|
2017-10-26 19:48:09 +08:00
|
|
|
drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
|
2017-04-14 18:13:34 +08:00
|
|
|
|
2019-06-03 16:32:02 +08:00
|
|
|
pm_runtime_disable(ddev->dev);
|
2017-04-14 18:13:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
|
|
|
|
MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
|
|
|
|
MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
|
|
|
|
MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
|
|
|
|
MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|