diff --git a/arch/arm/boot/dts/bcm2711.dtsi b/arch/arm/boot/dts/bcm2711.dtsi index 41698e7f5e243a..96ef133391d1cd 100644 --- a/arch/arm/boot/dts/bcm2711.dtsi +++ b/arch/arm/boot/dts/bcm2711.dtsi @@ -316,7 +316,8 @@ <0x7ef01f00 0x400>, <0x7ef00200 0x80>, <0x7ef04300 0x100>, - <0x7ef20000 0x100>; + <0x7ef20000 0x100>, + <0x7ef00100 0x30>; reg-names = "hdmi", "dvp", "phy", @@ -325,13 +326,15 @@ "metadata", "csc", "cec", - "hd"; + "hd", + "intr2"; clocks = <&firmware_clocks 13>; clock-names = "hdmi"; resets = <&dvp 0>; ddc = <&ddc0>; dmas = <&dma 10>; dma-names = "audio-rx"; + interrupts = ; status = "disabled"; }; @@ -353,7 +356,8 @@ <0x7ef06f00 0x400>, <0x7ef00280 0x80>, <0x7ef09300 0x100>, - <0x7ef20000 0x100>; + <0x7ef20000 0x100>, + <0x7ef00100 0x30>; reg-names = "hdmi", "dvp", "phy", @@ -362,13 +366,15 @@ "metadata", "csc", "cec", - "hd"; + "hd", + "intr2"; ddc = <&ddc1>; clocks = <&firmware_clocks 13>; clock-names = "hdmi"; resets = <&dvp 1>; dmas = <&dma 17>; dma-names = "audio-rx"; + interrupts = ; status = "disabled"; }; diff --git a/arch/arm/configs/bcm2711_defconfig b/arch/arm/configs/bcm2711_defconfig index 134c235b0fc2d1..311c8bfbaedf55 100644 --- a/arch/arm/configs/bcm2711_defconfig +++ b/arch/arm/configs/bcm2711_defconfig @@ -890,6 +890,7 @@ CONFIG_DRM_PANEL_SIMPLE=m CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m CONFIG_DRM_V3D=m CONFIG_DRM_VC4=m +CONFIG_DRM_VC4_HDMI_CEC=y CONFIG_TINYDRM_ILI9225=m CONFIG_TINYDRM_ILI9341=m CONFIG_TINYDRM_MI0283QT=m diff --git a/arch/arm64/configs/bcm2711_defconfig b/arch/arm64/configs/bcm2711_defconfig index 7c362f4355c554..35afa6b6ffd610 100644 --- a/arch/arm64/configs/bcm2711_defconfig +++ b/arch/arm64/configs/bcm2711_defconfig @@ -921,6 +921,7 @@ CONFIG_DRM_PANEL_SIMPLE=m CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN=m CONFIG_DRM_V3D=m CONFIG_DRM_VC4=m +CONFIG_DRM_VC4_HDMI_CEC=y CONFIG_TINYDRM_ILI9225=m CONFIG_TINYDRM_ILI9341=m CONFIG_TINYDRM_MI0283QT=m diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 87e6f2d867eaa8..2d608cf9dff921 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -79,6 +79,7 @@ # define VC4_HD_M_ENABLE BIT(0) #define CEC_CLOCK_FREQ 40000 +#define VC4_HSM_CLOCK 163682864 static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { @@ -755,8 +756,7 @@ static u32 vc4_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixe * needs to be a bit higher than the pixel clock rate * (generally 148.5Mhz). */ - - return 163682864; + return VC4_HSM_CLOCK; } static u32 vc5_hdmi_calc_hsm_clock(struct vc4_hdmi *vc4_hdmi, unsigned long pixel_rate) @@ -1264,8 +1264,13 @@ static void vc4_cec_read_msg(struct vc4_hdmi *vc4_hdmi, u32 cntrl1) msg->len = 1 + ((cntrl1 & VC4_HDMI_CEC_REC_WRD_CNT_MASK) >> VC4_HDMI_CEC_REC_WRD_CNT_SHIFT); + + if (msg->len > 16) { + DRM_ERROR("Attempting to read too much data (%d)\n", msg->len); + return; + } for (i = 0; i < msg->len; i += 4) { - u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + i); + u32 val = HDMI_READ(HDMI_CEC_RX_DATA_1 + (i>>2)); msg->msg[i] = val & 0xff; msg->msg[i + 1] = (val >> 8) & 0xff; @@ -1280,7 +1285,7 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) u32 stat = HDMI_READ(HDMI_CEC_CPU_STATUS); u32 cntrl1, cntrl5; - if (!(stat & VC4_HDMI_CPU_CEC)) + if (!(stat & vc4_hdmi->variant->cec_mask)) return IRQ_NONE; vc4_hdmi->cec_rx_msg.len = 0; cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); @@ -1296,7 +1301,7 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) cntrl1 &= ~VC4_HDMI_CEC_START_XMIT_BEGIN; } HDMI_WRITE(HDMI_CEC_CNTRL_1, cntrl1); - HDMI_WRITE(HDMI_CEC_CPU_CLEAR, VC4_HDMI_CPU_CEC); + HDMI_WRITE(HDMI_CEC_CPU_CLEAR, vc4_hdmi->variant->cec_mask); return IRQ_WAKE_THREAD; } @@ -1335,9 +1340,9 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) ((3600 / usecs) << VC4_HDMI_CEC_CNT_TO_3600_US_SHIFT) | ((3500 / usecs) << VC4_HDMI_CEC_CNT_TO_3500_US_SHIFT)); - HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, VC4_HDMI_CPU_CEC); + HDMI_WRITE(HDMI_CEC_CPU_MASK_CLEAR, vc4_hdmi->variant->cec_mask); } else { - HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC); + HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, vc4_hdmi->variant->cec_mask); HDMI_WRITE(HDMI_CEC_CNTRL_5, val | VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET); } @@ -1361,8 +1366,12 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 val; unsigned int i; + if (msg->len > 16) { + DRM_ERROR("Attempting to transmit too much data (%d)\n", msg->len); + return -ENOMEM; + } for (i = 0; i < msg->len; i += 4) - HDMI_WRITE(HDMI_CEC_TX_DATA_1 + i, + HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i>>2), (msg->msg[i]) | (msg->msg[i + 1] << 8) | (msg->msg[i + 2] << 16) | @@ -1390,11 +1399,9 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) struct cec_connector_info conn_info; struct platform_device *pdev = vc4_hdmi->pdev; u32 value; + u32 clk_cnt; int ret; - if (!vc4_hdmi->variant->cec_available) - return 0; - vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, vc4_hdmi, "vc4", CEC_CAP_DEFAULTS | @@ -1414,12 +1421,14 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) * divider: the hsm_clock rate and this divider setting will * give a 40 kHz CEC clock. */ + clk_cnt = vc4_hdmi->variant->cec_input_clock / CEC_CLOCK_FREQ; value |= VC4_HDMI_CEC_ADDR_MASK | - (4091 << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); + ((clk_cnt-1) << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT); HDMI_WRITE(HDMI_CEC_CNTRL_1, value); ret = devm_request_threaded_irq(&pdev->dev, platform_get_irq(pdev, 0), vc4_cec_irq_handler, - vc4_cec_irq_handler_thread, 0, + vc4_cec_irq_handler_thread, + IRQF_SHARED, "vc4 hdmi cec", vc4_hdmi); if (ret) goto err_delete_cec_adap; @@ -1572,6 +1581,14 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) if (IS_ERR(vc4_hdmi->dvp_regs)) return PTR_ERR(vc4_hdmi->dvp_regs); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr2"); + if (!res) + return -ENODEV; + + vc4_hdmi->intr2_regs = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(vc4_hdmi->intr2_regs)) + return PTR_ERR(vc4_hdmi->intr2_regs); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); if (!res) return -ENODEV; @@ -1751,8 +1768,8 @@ static int vc4_hdmi_dev_remove(struct platform_device *pdev) static const struct vc4_hdmi_variant bcm2835_variant = { .max_pixel_clock = 162000000, + .cec_input_clock = VC4_HSM_CLOCK, .audio_available = true, - .cec_available = true, .registers = vc4_hdmi_fields, .num_registers = ARRAY_SIZE(vc4_hdmi_fields), @@ -1767,12 +1784,15 @@ static const struct vc4_hdmi_variant bcm2835_variant = { .get_hsm_clock = vc4_hdmi_get_hsm_clock, .calc_hsm_clock = vc4_hdmi_calc_hsm_clock, .channel_map = vc4_hdmi_channel_map, + + .cec_mask = VC4_HDMI_CPU_CEC, }; static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { .id = 0, .audio_available = true, .max_pixel_clock = 297000000, + .cec_input_clock = 27000000, .registers = vc5_hdmi_hdmi0_fields, .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi0_fields), .phy_lane_mapping = { @@ -1792,12 +1812,15 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { .get_hsm_clock = vc5_hdmi_get_hsm_clock, .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, .channel_map = vc5_hdmi_channel_map, + + .cec_mask = VC5_HDMI0_CPU_CEC_RX | VC5_HDMI0_CPU_CEC_TX, }; static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { .id = 1, .audio_available = true, .max_pixel_clock = 297000000, + .cec_input_clock = 27000000, .registers = vc5_hdmi_hdmi1_fields, .num_registers = ARRAY_SIZE(vc5_hdmi_hdmi1_fields), .phy_lane_mapping = { @@ -1817,6 +1840,8 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { .get_hsm_clock = vc5_hdmi_get_hsm_clock, .calc_hsm_clock = vc5_hdmi_calc_hsm_clock, .channel_map = vc5_hdmi_channel_map, + + .cec_mask = VC5_HDMI1_CPU_CEC_RX | VC5_HDMI1_CPU_CEC_TX, }; static const struct of_device_id vc4_hdmi_dt_match[] = { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 95bed29531b9d2..60f7bf99e06ef0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -42,12 +42,12 @@ struct vc4_hdmi_variant { /* Set to true when the audio support is available */ bool audio_available; - /* Set to true when the CEC support is available */ - bool cec_available; - /* Maximum pixel clock supported by the controller (in Hz) */ unsigned long long max_pixel_clock; + /* Input clock frequency of CEC block (in Hz) */ + unsigned long cec_input_clock; + /* List of the registers available on that variant */ const struct vc4_hdmi_register *registers; @@ -97,6 +97,9 @@ struct vc4_hdmi_variant { /* Callback to get channel map */ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask); + + /* Bitmask for CEC events */ + u32 cec_mask; }; /* HDMI audio information */ @@ -140,6 +143,8 @@ struct vc4_hdmi { void __iomem *ram_regs; /* VC5 Only */ void __iomem *rm_regs; + /* VC5 Only */ + void __iomem *intr2_regs; int hpd_gpio; bool hpd_active_low; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index ea948ffaa69bc8..7311a8c0f1e504 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -24,6 +24,7 @@ enum vc4_hdmi_regs { VC5_PHY, VC5_RAM, VC5_RM, + VC5_INTR2, }; enum vc4_hdmi_field { @@ -33,11 +34,12 @@ enum vc4_hdmi_field { HDMI_CEC_CNTRL_3, HDMI_CEC_CNTRL_4, HDMI_CEC_CNTRL_5, + HDMI_CEC_CPU_STATUS, + HDMI_CEC_CPU_SET, HDMI_CEC_CPU_CLEAR, - HDMI_CEC_CPU_MASK_CLEAR, - HDMI_CEC_CPU_MASK_SET, HDMI_CEC_CPU_MASK_STATUS, - HDMI_CEC_CPU_STATUS, + HDMI_CEC_CPU_MASK_SET, + HDMI_CEC_CPU_MASK_CLEAR, /* * Transmit data, first byte is low byte of the 32-bit reg. @@ -147,6 +149,7 @@ struct vc4_hdmi_register { #define VC5_CEC_REG(reg, offset) _VC4_REG(VC5_CEC, reg, offset) #define VC5_CSC_REG(reg, offset) _VC4_REG(VC5_CSC, reg, offset) #define VC5_DVP_REG(reg, offset) _VC4_REG(VC5_DVP, reg, offset) +#define VC5_INTR2_REG(reg, offset) _VC4_REG(VC5_INTR2, reg, offset) #define VC5_PHY_REG(reg, offset) _VC4_REG(VC5_PHY, reg, offset) #define VC5_RAM_REG(reg, offset) _VC4_REG(VC5_RAM, reg, offset) #define VC5_RM_REG(reg, offset) _VC4_REG(VC5_RM, reg, offset) @@ -205,9 +208,10 @@ static const struct vc4_hdmi_register vc4_hdmi_fields[] = { VC4_HDMI_REG(HDMI_TX_PHY_RESET_CTL, 0x02c0), VC4_HDMI_REG(HDMI_TX_PHY_CTL_0, 0x02c4), VC4_HDMI_REG(HDMI_CEC_CPU_STATUS, 0x0340), + VC4_HDMI_REG(HDMI_CEC_CPU_SET, 0x0344), VC4_HDMI_REG(HDMI_CEC_CPU_CLEAR, 0x0348), VC4_HDMI_REG(HDMI_CEC_CPU_MASK_STATUS, 0x034c), - VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x034c), + VC4_HDMI_REG(HDMI_CEC_CPU_MASK_SET, 0x0350), VC4_HDMI_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0354), VC4_HDMI_REG(HDMI_RAM_PACKET_START, 0x0400), }; @@ -278,6 +282,12 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi0_fields[] = { VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), + VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000), + VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004), + VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014), VC5_CSC_REG(HDMI_CSC_CTL, 0x000), VC5_CSC_REG(HDMI_CSC_12_11, 0x004), @@ -354,6 +364,12 @@ static const struct vc4_hdmi_register vc5_hdmi_hdmi1_fields[] = { VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c), VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040), VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044), + VC5_INTR2_REG(HDMI_CEC_CPU_STATUS, 0x0000), + VC5_INTR2_REG(HDMI_CEC_CPU_SET, 0x0004), + VC5_INTR2_REG(HDMI_CEC_CPU_CLEAR, 0x0008), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_STATUS, 0x000c), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_SET, 0x0010), + VC5_INTR2_REG(HDMI_CEC_CPU_MASK_CLEAR, 0x0014), VC5_CSC_REG(HDMI_CSC_CTL, 0x000), VC5_CSC_REG(HDMI_CSC_12_11, 0x004), @@ -384,6 +400,9 @@ void __iomem *__vc4_hdmi_get_field_base(struct vc4_hdmi *hdmi, case VC5_DVP: return hdmi->dvp_regs; + case VC5_INTR2: + return hdmi->intr2_regs; + case VC5_PHY: return hdmi->phy_regs; diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 78809866097c82..4d017572a5c28f 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -668,6 +668,15 @@ # define VC4_HDMI_CPU_CEC BIT(6) # define VC4_HDMI_CPU_HOTPLUG BIT(0) +# define VC5_HDMI0_CPU_CEC_RX BIT(1) +# define VC5_HDMI0_CPU_CEC_TX BIT(0) +# define VC5_HDMI0_CPU_HOTPLUG_CONN BIT(4) +# define VC5_HDMI0_CPU_HOTPLUG_REM BIT(5) +# define VC5_HDMI1_CPU_CEC_RX BIT(7) +# define VC5_HDMI1_CPU_CEC_TX BIT(6) +# define VC5_HDMI1_CPU_HOTPLUG_CONN BIT(10) +# define VC5_HDMI1_CPU_HOTPLUG_REM BIT(11) + /* Debug: Current receive value on the CEC pad. */ # define VC4_HD_CECRXD BIT(9) /* Debug: Override CEC output to 0. */