27,8 → 27,8 |
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/i2c.h> |
#include <drm/drmP.h> |
#include <drm/drm_dp_helper.h> |
#include <drm/drmP.h> |
|
/** |
* DOC: dp helpers |
206,7 → 206,7 |
* i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper |
* @adapter: i2c adapter to register |
* |
* This registers an i2c adapater that uses dp aux channel as it's underlaying |
* This registers an i2c adapter that uses dp aux channel as it's underlaying |
* transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure |
* and store it in the algo_data member of the @adapter argument. This will be |
* used by the i2c over dp aux algorithm to drive the hardware. |
213,6 → 213,10 |
* |
* RETURNS: |
* 0 on success, -ERRNO on failure. |
* |
* IMPORTANT: |
* This interface is deprecated, please switch to the new dp aux helpers and |
* drm_dp_aux_register(). |
*/ |
int |
i2c_dp_aux_add_bus(struct i2c_adapter *adapter) |
346,3 → 350,421 |
} |
} |
EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); |
|
/** |
* DOC: dp helpers |
* |
* The DisplayPort AUX channel is an abstraction to allow generic, driver- |
* independent access to AUX functionality. Drivers can take advantage of |
* this by filling in the fields of the drm_dp_aux structure. |
* |
* Transactions are described using a hardware-independent drm_dp_aux_msg |
* structure, which is passed into a driver's .transfer() implementation. |
* Both native and I2C-over-AUX transactions are supported. |
*/ |
|
static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, |
unsigned int offset, void *buffer, size_t size) |
{ |
struct drm_dp_aux_msg msg; |
unsigned int retry; |
int err; |
|
memset(&msg, 0, sizeof(msg)); |
msg.address = offset; |
msg.request = request; |
msg.buffer = buffer; |
msg.size = size; |
|
/* |
* The specification doesn't give any recommendation on how often to |
* retry native transactions, so retry 7 times like for I2C-over-AUX |
* transactions. |
*/ |
for (retry = 0; retry < 7; retry++) { |
|
mutex_lock(&aux->hw_mutex); |
err = aux->transfer(aux, &msg); |
mutex_unlock(&aux->hw_mutex); |
if (err < 0) { |
if (err == -EBUSY) |
continue; |
|
return err; |
} |
|
|
switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { |
case DP_AUX_NATIVE_REPLY_ACK: |
if (err < size) |
return -EPROTO; |
return err; |
|
case DP_AUX_NATIVE_REPLY_NACK: |
return -EIO; |
|
case DP_AUX_NATIVE_REPLY_DEFER: |
usleep(500); |
break; |
} |
} |
|
DRM_DEBUG_KMS("too many retries, giving up\n"); |
return -EIO; |
} |
|
/** |
* drm_dp_dpcd_read() - read a series of bytes from the DPCD |
* @aux: DisplayPort AUX channel |
* @offset: address of the (first) register to read |
* @buffer: buffer to store the register values |
* @size: number of bytes in @buffer |
* |
* Returns the number of bytes transferred on success, or a negative error |
* code on failure. -EIO is returned if the request was NAKed by the sink or |
* if the retry count was exceeded. If not all bytes were transferred, this |
* function returns -EPROTO. Errors from the underlying AUX channel transfer |
* function, with the exception of -EBUSY (which causes the transaction to |
* be retried), are propagated to the caller. |
*/ |
ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, |
void *buffer, size_t size) |
{ |
return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, |
size); |
} |
EXPORT_SYMBOL(drm_dp_dpcd_read); |
|
/** |
* drm_dp_dpcd_write() - write a series of bytes to the DPCD |
* @aux: DisplayPort AUX channel |
* @offset: address of the (first) register to write |
* @buffer: buffer containing the values to write |
* @size: number of bytes in @buffer |
* |
* Returns the number of bytes transferred on success, or a negative error |
* code on failure. -EIO is returned if the request was NAKed by the sink or |
* if the retry count was exceeded. If not all bytes were transferred, this |
* function returns -EPROTO. Errors from the underlying AUX channel transfer |
* function, with the exception of -EBUSY (which causes the transaction to |
* be retried), are propagated to the caller. |
*/ |
ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, |
void *buffer, size_t size) |
{ |
return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, |
size); |
} |
EXPORT_SYMBOL(drm_dp_dpcd_write); |
|
/** |
* drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) |
* @aux: DisplayPort AUX channel |
* @status: buffer to store the link status in (must be at least 6 bytes) |
* |
* Returns the number of bytes transferred on success or a negative error |
* code on failure. |
*/ |
int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, |
u8 status[DP_LINK_STATUS_SIZE]) |
{ |
return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status, |
DP_LINK_STATUS_SIZE); |
} |
EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); |
|
/** |
* drm_dp_link_probe() - probe a DisplayPort link for capabilities |
* @aux: DisplayPort AUX channel |
* @link: pointer to structure in which to return link capabilities |
* |
* The structure filled in by this function can usually be passed directly |
* into drm_dp_link_power_up() and drm_dp_link_configure() to power up and |
* configure the link based on the link's capabilities. |
* |
* Returns 0 on success or a negative error code on failure. |
*/ |
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) |
{ |
u8 values[3]; |
int err; |
|
memset(link, 0, sizeof(*link)); |
|
err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); |
if (err < 0) |
return err; |
|
link->revision = values[0]; |
link->rate = drm_dp_bw_code_to_link_rate(values[1]); |
link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; |
|
if (values[2] & DP_ENHANCED_FRAME_CAP) |
link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; |
|
return 0; |
} |
EXPORT_SYMBOL(drm_dp_link_probe); |
|
/** |
* drm_dp_link_power_up() - power up a DisplayPort link |
* @aux: DisplayPort AUX channel |
* @link: pointer to a structure containing the link configuration |
* |
* Returns 0 on success or a negative error code on failure. |
*/ |
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) |
{ |
u8 value; |
int err; |
|
/* DP_SET_POWER register is only available on DPCD v1.1 and later */ |
if (link->revision < 0x11) |
return 0; |
|
err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); |
if (err < 0) |
return err; |
|
value &= ~DP_SET_POWER_MASK; |
value |= DP_SET_POWER_D0; |
|
err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); |
if (err < 0) |
return err; |
|
/* |
* According to the DP 1.1 specification, a "Sink Device must exit the |
* power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink |
* Control Field" (register 0x600). |
*/ |
usleep(2000); |
|
return 0; |
} |
EXPORT_SYMBOL(drm_dp_link_power_up); |
|
/** |
* drm_dp_link_configure() - configure a DisplayPort link |
* @aux: DisplayPort AUX channel |
* @link: pointer to a structure containing the link configuration |
* |
* Returns 0 on success or a negative error code on failure. |
*/ |
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) |
{ |
u8 values[2]; |
int err; |
|
values[0] = drm_dp_link_rate_to_bw_code(link->rate); |
values[1] = link->num_lanes; |
|
if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) |
values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
|
err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); |
if (err < 0) |
return err; |
|
return 0; |
} |
EXPORT_SYMBOL(drm_dp_link_configure); |
|
/* |
* I2C-over-AUX implementation |
*/ |
|
static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) |
{ |
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | |
I2C_FUNC_SMBUS_READ_BLOCK_DATA | |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL | |
I2C_FUNC_10BIT_ADDR; |
} |
|
/* |
* Transfer a single I2C-over-AUX message and handle various error conditions, |
* retrying the transaction as appropriate. It is assumed that the |
* aux->transfer function does not modify anything in the msg other than the |
* reply field. |
*/ |
static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) |
{ |
unsigned int retry; |
int err; |
|
/* |
* DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device |
* is required to retry at least seven times upon receiving AUX_DEFER |
* before giving up the AUX transaction. |
*/ |
for (retry = 0; retry < 7; retry++) { |
mutex_lock(&aux->hw_mutex); |
err = aux->transfer(aux, msg); |
mutex_unlock(&aux->hw_mutex); |
if (err < 0) { |
if (err == -EBUSY) |
continue; |
|
DRM_DEBUG_KMS("transaction failed: %d\n", err); |
return err; |
} |
|
|
switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { |
case DP_AUX_NATIVE_REPLY_ACK: |
/* |
* For I2C-over-AUX transactions this isn't enough, we |
* need to check for the I2C ACK reply. |
*/ |
break; |
|
case DP_AUX_NATIVE_REPLY_NACK: |
DRM_DEBUG_KMS("native nack\n"); |
return -EREMOTEIO; |
|
case DP_AUX_NATIVE_REPLY_DEFER: |
DRM_DEBUG_KMS("native defer"); |
/* |
* We could check for I2C bit rate capabilities and if |
* available adjust this interval. We could also be |
* more careful with DP-to-legacy adapters where a |
* long legacy cable may force very low I2C bit rates. |
* |
* For now just defer for long enough to hopefully be |
* safe for all use-cases. |
*/ |
usleep_range(500, 600); |
continue; |
|
default: |
DRM_ERROR("invalid native reply %#04x\n", msg->reply); |
return -EREMOTEIO; |
} |
|
switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { |
case DP_AUX_I2C_REPLY_ACK: |
/* |
* Both native ACK and I2C ACK replies received. We |
* can assume the transfer was successful. |
*/ |
if (err < msg->size) |
return -EPROTO; |
return 0; |
|
case DP_AUX_I2C_REPLY_NACK: |
DRM_DEBUG_KMS("I2C nack\n"); |
return -EREMOTEIO; |
|
case DP_AUX_I2C_REPLY_DEFER: |
DRM_DEBUG_KMS("I2C defer\n"); |
usleep_range(400, 500); |
continue; |
|
default: |
DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); |
return -EREMOTEIO; |
} |
} |
|
DRM_DEBUG_KMS("too many retries, giving up\n"); |
return -EREMOTEIO; |
} |
|
static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, |
int num) |
{ |
struct drm_dp_aux *aux = adapter->algo_data; |
unsigned int i, j; |
struct drm_dp_aux_msg msg; |
int err = 0; |
|
memset(&msg, 0, sizeof(msg)); |
|
for (i = 0; i < num; i++) { |
msg.address = msgs[i].addr; |
msg.request = (msgs[i].flags & I2C_M_RD) ? |
DP_AUX_I2C_READ : |
DP_AUX_I2C_WRITE; |
msg.request |= DP_AUX_I2C_MOT; |
/* Send a bare address packet to start the transaction. |
* Zero sized messages specify an address only (bare |
* address) transaction. |
*/ |
msg.buffer = NULL; |
msg.size = 0; |
err = drm_dp_i2c_do_msg(aux, &msg); |
if (err < 0) |
break; |
/* |
* Many hardware implementations support FIFOs larger than a |
* single byte, but it has been empirically determined that |
* transferring data in larger chunks can actually lead to |
* decreased performance. Therefore each message is simply |
* transferred byte-by-byte. |
*/ |
for (j = 0; j < msgs[i].len; j++) { |
msg.buffer = msgs[i].buf + j; |
msg.size = 1; |
|
err = drm_dp_i2c_do_msg(aux, &msg); |
if (err < 0) |
break; |
} |
if (err < 0) |
break; |
} |
if (err >= 0) |
err = num; |
/* Send a bare address packet to close out the transaction. |
* Zero sized messages specify an address only (bare |
* address) transaction. |
*/ |
msg.request &= ~DP_AUX_I2C_MOT; |
msg.buffer = NULL; |
msg.size = 0; |
(void)drm_dp_i2c_do_msg(aux, &msg); |
|
return err; |
} |
|
static const struct i2c_algorithm drm_dp_i2c_algo = { |
.functionality = drm_dp_i2c_functionality, |
.master_xfer = drm_dp_i2c_xfer, |
}; |
|
/** |
* drm_dp_aux_register() - initialise and register aux channel |
* @aux: DisplayPort AUX channel |
* |
* Returns 0 on success or a negative error code on failure. |
*/ |
int drm_dp_aux_register(struct drm_dp_aux *aux) |
{ |
mutex_init(&aux->hw_mutex); |
|
aux->ddc.algo = &drm_dp_i2c_algo; |
aux->ddc.algo_data = aux; |
aux->ddc.retries = 3; |
|
aux->ddc.class = I2C_CLASS_DDC; |
aux->ddc.owner = THIS_MODULE; |
aux->ddc.dev.parent = aux->dev; |
// aux->ddc.dev.of_node = aux->dev->of_node; |
|
strlcpy(aux->ddc.name, aux->name ? aux->name : "aux", |
sizeof(aux->ddc.name)); |
|
return i2c_add_adapter(&aux->ddc); |
} |
EXPORT_SYMBOL(drm_dp_aux_register); |
|
/** |
* drm_dp_aux_unregister() - unregister an AUX adapter |
* @aux: DisplayPort AUX channel |
*/ |
void drm_dp_aux_unregister(struct drm_dp_aux *aux) |
{ |
i2c_del_adapter(&aux->ddc); |
} |
EXPORT_SYMBOL(drm_dp_aux_unregister); |