Allwinner DRM changes for 4.14

A few changes, but most notably improving the HDMI support merged in 4.13,
by reporting the DDC adapter as an i2c bus, and by adding CEC support
through the CEC framework.

* tag 'sunxi-drm-for-4.14' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
  sun4i_hdmi: add CEC support
  dt-bindings: display: sunxi: Improve endpoint ID scheme readability
  drm/sun4i: tcon: remove unused function
  drm/sun4i: Remove useless atomic_check
  drm/sun4i: Add if statement instead of depends on
  drm/sun4i: hdmi: Implement I2C adapter for A10s DDC bus
  drm/sun4i: constify drm_plane_helper_funcs
This commit is contained in:
Dave Airlie
2017-08-21 09:05:01 +10:00
10 changed files with 358 additions and 128 deletions

View File

@@ -13,17 +13,26 @@ config DRM_SUN4I
Display Engine. If M is selected the module will be called
sun4i-drm.
if DRM_SUN4I
config DRM_SUN4I_HDMI
tristate "Allwinner A10 HDMI Controller Support"
depends on DRM_SUN4I
default DRM_SUN4I
help
Choose this option if you have an Allwinner SoC with an HDMI
controller.
config DRM_SUN4I_HDMI_CEC
bool "Allwinner A10 HDMI CEC Support"
depends on DRM_SUN4I_HDMI
select CEC_CORE
depends on CEC_PIN
help
Choose this option if you have an Allwinner SoC with an HDMI
controller and want to use CEC.
config DRM_SUN4I_BACKEND
tristate "Support for Allwinner A10 Display Engine Backend"
depends on DRM_SUN4I
default DRM_SUN4I
help
Choose this option if you have an Allwinner SoC with the
@@ -33,10 +42,11 @@ config DRM_SUN4I_BACKEND
config DRM_SUN8I_MIXER
tristate "Support for Allwinner Display Engine 2.0 Mixer"
depends on DRM_SUN4I
default MACH_SUN8I
help
Choose this option if you have an Allwinner SoC with the
Allwinner Display Engine 2.0, which has a mixer to do some
graphics mixture and feed graphics to TCON, If M is
selected the module will be called sun8i-mixer.
endif

View File

@@ -2,6 +2,7 @@ sun4i-drm-y += sun4i_drv.o
sun4i-drm-y += sun4i_framebuffer.o
sun4i-drm-hdmi-y += sun4i_hdmi_enc.o
sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o
sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o
sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o

View File

@@ -15,6 +15,8 @@
#include <drm/drm_connector.h>
#include <drm/drm_encoder.h>
#include <media/cec.h>
#define SUN4I_HDMI_CTRL_REG 0x004
#define SUN4I_HDMI_CTRL_ENABLE BIT(31)
@@ -86,6 +88,11 @@
#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_MASK BIT(21)
#define SUN4I_HDMI_PLL_DBG0_TMDS_PARENT_SHIFT 21
#define SUN4I_HDMI_CEC 0x214
#define SUN4I_HDMI_CEC_ENABLE BIT(11)
#define SUN4I_HDMI_CEC_TX BIT(9)
#define SUN4I_HDMI_CEC_RX BIT(8)
#define SUN4I_HDMI_PKT_CTRL_REG(n) (0x2f0 + (4 * (n)))
#define SUN4I_HDMI_PKT_CTRL_TYPE(n, t) ((t) << (((n) % 4) * 4))
@@ -96,6 +103,7 @@
#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31)
#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30)
#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8)
#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8)
#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8)
#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0)
@@ -105,14 +113,34 @@
#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8)
#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff)
#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c
#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7)
#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6)
#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5)
#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4)
#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3)
#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2)
#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1)
#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0)
#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510
#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31)
#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4)
#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4)
#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1)
#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf)
#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0)
#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1)
#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518
#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c
#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1)
#define SUN4I_HDMI_DDC_CMD_REG 0x520
#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6
#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5
#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3
#define SUN4I_HDMI_DDC_CLK_REG 0x528
#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3)
@@ -146,12 +174,16 @@ struct sun4i_hdmi {
struct clk *ddc_clk;
struct clk *tmds_clk;
struct i2c_adapter *i2c;
struct sun4i_drv *drv;
bool hdmi_monitor;
struct cec_adapter *cec_adap;
};
int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
int sun4i_tmds_create(struct sun4i_hdmi *hdmi);
int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi);
#endif /* _SUN4I_HDMI_H_ */

View File

@@ -29,8 +29,6 @@
#include "sun4i_hdmi.h"
#include "sun4i_tcon.h"
#define DDC_SEGMENT_ADDR 0x30
static inline struct sun4i_hdmi *
drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
{
@@ -184,93 +182,13 @@ static const struct drm_encoder_funcs sun4i_hdmi_funcs = {
.destroy = drm_encoder_cleanup,
};
static int sun4i_hdmi_read_sub_block(struct sun4i_hdmi *hdmi,
unsigned int blk, unsigned int offset,
u8 *buf, unsigned int count)
{
unsigned long reg;
int i;
reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;
writel(reg | SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ,
hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
writel(SUN4I_HDMI_DDC_ADDR_SEGMENT(offset >> 8) |
SUN4I_HDMI_DDC_ADDR_EDDC(DDC_SEGMENT_ADDR << 1) |
SUN4I_HDMI_DDC_ADDR_OFFSET(offset) |
SUN4I_HDMI_DDC_ADDR_SLAVE(DDC_ADDR),
hdmi->base + SUN4I_HDMI_DDC_ADDR_REG);
reg = readl(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
writel(reg | SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR,
hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
writel(count, hdmi->base + SUN4I_HDMI_DDC_BYTE_COUNT_REG);
writel(SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ,
hdmi->base + SUN4I_HDMI_DDC_CMD_REG);
reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
writel(reg | SUN4I_HDMI_DDC_CTRL_START_CMD,
hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
!(reg & SUN4I_HDMI_DDC_CTRL_START_CMD),
100, 100000))
return -EIO;
for (i = 0; i < count; i++)
buf[i] = readb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG);
return 0;
}
static int sun4i_hdmi_read_edid_block(void *data, u8 *buf, unsigned int blk,
size_t length)
{
struct sun4i_hdmi *hdmi = data;
int retry = 2, i;
do {
for (i = 0; i < length; i += SUN4I_HDMI_DDC_FIFO_SIZE) {
unsigned char offset = blk * EDID_LENGTH + i;
unsigned int count = min((unsigned int)SUN4I_HDMI_DDC_FIFO_SIZE,
length - i);
int ret;
ret = sun4i_hdmi_read_sub_block(hdmi, blk, offset,
buf + i, count);
if (ret)
return ret;
}
} while (!drm_edid_block_valid(buf, blk, true, NULL) && (retry--));
return 0;
}
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
{
struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
unsigned long reg;
struct edid *edid;
int ret;
/* Reset i2c controller */
writel(SUN4I_HDMI_DDC_CTRL_ENABLE | SUN4I_HDMI_DDC_CTRL_RESET,
hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
!(reg & SUN4I_HDMI_DDC_CTRL_RESET),
100, 2000))
return -EIO;
writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE |
SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,
hdmi->base + SUN4I_HDMI_DDC_LINE_CTRL_REG);
clk_prepare_enable(hdmi->ddc_clk);
clk_set_rate(hdmi->ddc_clk, 100000);
edid = drm_do_get_edid(connector, sun4i_hdmi_read_edid_block, hdmi);
edid = drm_get_edid(connector, hdmi->i2c);
if (!edid)
return 0;
@@ -279,11 +197,10 @@ static int sun4i_hdmi_get_modes(struct drm_connector *connector)
hdmi->hdmi_monitor ? "an HDMI" : "a DVI");
drm_mode_connector_update_edid_property(connector, edid);
cec_s_phys_addr_from_edid(hdmi->cec_adap, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
clk_disable_unprepare(hdmi->ddc_clk);
return ret;
}
@@ -299,8 +216,10 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force)
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_HPD_REG, reg,
reg & SUN4I_HDMI_HPD_HIGH,
0, 500000))
0, 500000)) {
cec_phys_addr_invalidate(hdmi->cec_adap);
return connector_status_disconnected;
}
return connector_status_connected;
}
@@ -314,6 +233,40 @@ static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
static bool sun4i_hdmi_cec_pin_read(struct cec_adapter *adap)
{
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX;
}
static void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap)
{
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
/* Start driving the CEC pin low */
writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC);
}
static void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap)
{
struct sun4i_hdmi *hdmi = cec_get_drvdata(adap);
/*
* Stop driving the CEC pin, the pull up will take over
* unless another CEC device is driving the pin low.
*/
writel(0, hdmi->base + SUN4I_HDMI_CEC);
}
static const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = {
.read = sun4i_hdmi_cec_pin_read,
.low = sun4i_hdmi_cec_pin_low,
.high = sun4i_hdmi_cec_pin_high,
};
#endif
static int sun4i_hdmi_bind(struct device *dev, struct device *master,
void *data)
{
@@ -406,9 +359,9 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
SUN4I_HDMI_PLL_CTRL_PLL_EN;
writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
ret = sun4i_ddc_create(hdmi, hdmi->tmds_clk);
ret = sun4i_hdmi_i2c_create(dev, hdmi);
if (ret) {
dev_err(dev, "Couldn't create the DDC clock\n");
dev_err(dev, "Couldn't create the HDMI I2C adapter\n");
return ret;
}
@@ -421,13 +374,26 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
NULL);
if (ret) {
dev_err(dev, "Couldn't initialise the HDMI encoder\n");
return ret;
goto err_del_i2c_adapter;
}
hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm,
dev->of_node);
if (!hdmi->encoder.possible_crtcs)
return -EPROBE_DEFER;
if (!hdmi->encoder.possible_crtcs) {
ret = -EPROBE_DEFER;
goto err_del_i2c_adapter;
}
#ifdef CONFIG_DRM_SUN4I_HDMI_CEC
hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops,
hdmi, "sun4i", CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
CEC_CAP_PASSTHROUGH | CEC_CAP_RC);
ret = PTR_ERR_OR_ZERO(hdmi->cec_adap);
if (ret < 0)
goto err_cleanup_connector;
writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX,
hdmi->base + SUN4I_HDMI_CEC);
#endif
drm_connector_helper_add(&hdmi->connector,
&sun4i_hdmi_connector_helper_funcs);
@@ -444,12 +410,18 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master,
hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
ret = cec_register_adapter(hdmi->cec_adap, dev);
if (ret < 0)
goto err_cleanup_connector;
drm_mode_connector_attach_encoder(&hdmi->connector, &hdmi->encoder);
return 0;
err_cleanup_connector:
cec_delete_adapter(hdmi->cec_adap);
drm_encoder_cleanup(&hdmi->encoder);
err_del_i2c_adapter:
i2c_del_adapter(hdmi->i2c);
return ret;
}
@@ -458,8 +430,10 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master,
{
struct sun4i_hdmi *hdmi = dev_get_drvdata(dev);
cec_unregister_adapter(hdmi->cec_adap);
drm_connector_cleanup(&hdmi->connector);
drm_encoder_cleanup(&hdmi->encoder);
i2c_del_adapter(hdmi->i2c);
}
static const struct component_ops sun4i_hdmi_ops = {

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com>
* Copyright (C) 2017 Jonathan Liu <net147@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/iopoll.h>
#include "sun4i_hdmi.h"
#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \
SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \
SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \
SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \
SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \
SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \
SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \
)
/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */
#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX
/* FIFO request bit is set when FIFO level is below TX_THRESHOLD during write */
#define TX_THRESHOLD 1
static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read)
{
/*
* 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz
* clock. As clock rate is fixed, just round it up to 100 us.
*/
const unsigned long byte_time_ns = 100;
const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |
SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |
SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE;
u32 reg;
/* Limit transfer length by FIFO threshold */
len = min_t(int, len, read ? (RX_THRESHOLD + 1) :
(SUN4I_HDMI_DDC_FIFO_SIZE - TX_THRESHOLD + 1));
/* Wait until error, FIFO request bit set or transfer complete */
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG, reg,
reg & mask, len * byte_time_ns, 100000))
return -ETIMEDOUT;
if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK)
return -EIO;
if (read)
readsb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG, buf, len);
else
writesb(hdmi->base + SUN4I_HDMI_DDC_FIFO_DATA_REG, buf, len);
/* Clear FIFO request bit */
writel(SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST,
hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
return len;
}
static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg)
{
int i, len;
u32 reg;
/* Set FIFO direction */
reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK;
reg |= (msg->flags & I2C_M_RD) ?
SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ :
SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE;
writel(reg, hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
/* Set I2C address */
writel(SUN4I_HDMI_DDC_ADDR_SLAVE(msg->addr),
hdmi->base + SUN4I_HDMI_DDC_ADDR_REG);
/* Set FIFO RX/TX thresholds and clear FIFO */
reg = readl(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
reg |= SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR;
reg &= ~SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK;
reg |= SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(RX_THRESHOLD);
reg &= ~SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK;
reg |= SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(TX_THRESHOLD);
writel(reg, hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG);
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_FIFO_CTRL_REG,
reg,
!(reg & SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR),
100, 2000))
return -EIO;
/* Set transfer length */
writel(msg->len, hdmi->base + SUN4I_HDMI_DDC_BYTE_COUNT_REG);
/* Set command */
writel(msg->flags & I2C_M_RD ?
SUN4I_HDMI_DDC_CMD_IMPLICIT_READ :
SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE,
hdmi->base + SUN4I_HDMI_DDC_CMD_REG);
/* Clear interrupt status bits */
writel(SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK |
SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST |
SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE,
hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
/* Start command */
reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
writel(reg | SUN4I_HDMI_DDC_CTRL_START_CMD,
hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
/* Transfer bytes */
for (i = 0; i < msg->len; i += len) {
len = fifo_transfer(hdmi, msg->buf + i, msg->len - i,
msg->flags & I2C_M_RD);
if (len <= 0)
return len;
}
/* Wait for command to finish */
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG,
reg,
!(reg & SUN4I_HDMI_DDC_CTRL_START_CMD),
100, 100000))
return -EIO;
/* Check for errors */
reg = readl(hdmi->base + SUN4I_HDMI_DDC_INT_STATUS_REG);
if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) ||
!(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) {
return -EIO;
}
return 0;
}
static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct sun4i_hdmi *hdmi = i2c_get_adapdata(adap);
u32 reg;
int err, i, ret = num;
for (i = 0; i < num; i++) {
if (!msgs[i].len)
return -EINVAL;
if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX)
return -EINVAL;
}
/* Reset I2C controller */
writel(SUN4I_HDMI_DDC_CTRL_ENABLE | SUN4I_HDMI_DDC_CTRL_RESET,
hdmi->base + SUN4I_HDMI_DDC_CTRL_REG);
if (readl_poll_timeout(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG, reg,
!(reg & SUN4I_HDMI_DDC_CTRL_RESET),
100, 2000))
return -EIO;
writel(SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE |
SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE,
hdmi->base + SUN4I_HDMI_DDC_LINE_CTRL_REG);
clk_prepare_enable(hdmi->ddc_clk);
clk_set_rate(hdmi->ddc_clk, 100000);
for (i = 0; i < num; i++) {
err = xfer_msg(hdmi, &msgs[i]);
if (err) {
ret = err;
break;
}
}
clk_disable_unprepare(hdmi->ddc_clk);
return ret;
}
static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = {
.master_xfer = sun4i_hdmi_i2c_xfer,
.functionality = sun4i_hdmi_i2c_func,
};
int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi)
{
struct i2c_adapter *adap;
int ret = 0;
ret = sun4i_ddc_create(hdmi, hdmi->tmds_clk);
if (ret)
return ret;
adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
if (!adap)
return -ENOMEM;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_DDC;
adap->algo = &sun4i_hdmi_i2c_algorithm;
strlcpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name));
i2c_set_adapdata(adap, hdmi);
ret = i2c_add_adapter(adap);
if (ret)
return ret;
hdmi->i2c = adap;
return ret;
}

View File

@@ -25,12 +25,6 @@ struct sun4i_plane_desc {
uint32_t nformats;
};
static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
return 0;
}
static void sun4i_backend_layer_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
@@ -52,8 +46,7 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane,
sun4i_backend_layer_enable(backend, layer->id, true);
}
static struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
.atomic_check = sun4i_backend_layer_atomic_check,
static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = {
.atomic_disable = sun4i_backend_layer_atomic_disable,
.atomic_update = sun4i_backend_layer_atomic_update,
};

View File

@@ -127,13 +127,6 @@ static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int sun4i_rgb_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return 0;
}
static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
{
struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
@@ -181,7 +174,6 @@ static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
}
static struct drm_encoder_helper_funcs sun4i_rgb_enc_helper_funcs = {
.atomic_check = sun4i_rgb_atomic_check,
.mode_set = sun4i_rgb_encoder_mode_set,
.disable = sun4i_rgb_encoder_disable,
.enable = sun4i_rgb_encoder_enable,

View File

@@ -194,8 +194,6 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel);
void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
/* Mode Related Controls */
void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon,
bool enable);
void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
struct drm_encoder *encoder);
void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,

View File

@@ -341,13 +341,6 @@ static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
mode->vtotal = mode->vsync_end + tv_mode->vback_porch;
}
static int sun4i_tv_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return 0;
}
static void sun4i_tv_disable(struct drm_encoder *encoder)
{
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
@@ -489,7 +482,6 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
}
static struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
.atomic_check = sun4i_tv_atomic_check,
.disable = sun4i_tv_disable,
.enable = sun4i_tv_enable,
.mode_set = sun4i_tv_mode_set,