Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vc4_hdmi: Add CEC support for 2711 #3601

Merged
merged 8 commits into from
May 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions arch/arm/boot/dts/bcm2711.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@
<0x7ef01f00 0x400>,
<0x7ef00200 0x80>,
<0x7ef04300 0x100>,
<0x7ef20000 0x100>;
<0x7ef20000 0x100>,
<0x7ef00100 0x30>;
reg-names = "hdmi",
"dvp",
"phy",
Expand All @@ -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 = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};

Expand All @@ -353,7 +356,8 @@
<0x7ef06f00 0x400>,
<0x7ef00280 0x80>,
<0x7ef09300 0x100>,
<0x7ef20000 0x100>;
<0x7ef20000 0x100>,
<0x7ef00100 0x30>;
reg-names = "hdmi",
"dvp",
"phy",
Expand All @@ -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 = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};

Expand Down
1 change: 1 addition & 0 deletions arch/arm/configs/bcm2711_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not also applicable to 64-bit builds?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Untested but no reason I can think of why 64-bit build would fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw it in there, then.

CONFIG_TINYDRM_ILI9225=m
CONFIG_TINYDRM_ILI9341=m
CONFIG_TINYDRM_MI0283QT=m
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/configs/bcm2711_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 39 additions & 14 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct for Pi3 as well? It appears to be unconditional on revision.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is a bug on Pi3. Any long messages would trample other registers (and we are getting longer messages in log).


msg->msg[i] = val & 0xff;
msg->msg[i + 1] = (val >> 8) & 0xff;
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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) |
Expand Down Expand Up @@ -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 |
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),

Expand All @@ -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 = {
Expand All @@ -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 = {
Expand All @@ -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[] = {
Expand Down
11 changes: 8 additions & 3 deletions drivers/gpu/drm/vc4/vc4_hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
27 changes: 23 additions & 4 deletions drivers/gpu/drm/vc4/vc4_hdmi_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ enum vc4_hdmi_regs {
VC5_PHY,
VC5_RAM,
VC5_RM,
VC5_INTR2,
};

enum vc4_hdmi_field {
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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),
};
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;

Expand Down
9 changes: 9 additions & 0 deletions drivers/gpu/drm/vc4/vc4_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down