Message ID | 1387297207-7643-2-git-send-email-treding@nvidia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Tue, 17 Dec 2013, Thierry Reding <thierry.reding@gmail.com> wrote: > This is a superset of the current i2c_dp_aux bus functionality and can > be used to transfer native AUX in addition to I2C-over-AUX messages. > > Helpers are provided to read and write the DPCD, either blockwise or > byte-wise. Many of the existing helpers for DisplayPort take a copy of a > portion of the DPCD and operate on that, without a way to write data > back to the DPCD (e.g. for configuration of the link). > > Subsequent patches will build upon this infrastructure to provide common > functionality in a generic way. > > Signed-off-by: Thierry Reding <treding@nvidia.com> > --- > drivers/gpu/drm/drm_dp_helper.c | 116 ++++++++++++++++++++++++++++++++++++++++ > include/drm/drm_dp_helper.h | 64 ++++++++++++++++++++++ > 2 files changed, 180 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c > index 9e978aae8972..bd622d462ca7 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -346,3 +346,119 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw) > } > } > EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); > + > +/** > + * 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. > + */ > +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_READ; > + 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++) { > + err = aux->transfer(aux, &msg); > + if (err < 0) { > + if (err == -EBUSY) > + continue; > + > + return err; > + } > + > + if (err == 0) > + return -EPROTO; > + > + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { > + case DP_AUX_NATIVE_REPLY_ACK: > + return err; > + > + case DP_AUX_NATIVE_REPLY_NACK: > + return -EIO; > + > + case DP_AUX_NATIVE_REPLY_DEFER: > + usleep_range(400, 500); > + break; > + } > + } > + > + DRM_ERROR("too many retries, giving up\n"); > + return -EIO; > +} > +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. > + */ > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_WRITE; > + 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++) { > + err = aux->transfer(aux, &msg); > + if (err < 0) { > + if (err == -EBUSY) > + continue; > + > + return err; > + } > + > + if (err == 0) > + return -EPROTO; > + > + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { > + case DP_AUX_NATIVE_REPLY_ACK: > + return 0; "Returns the number of bytes transferred on success, or a negative error code on failure." Compare drm_dp_dpcd_read. You could add an internal helper to do both read and write, it's mostly the same code. BR, Jani. > + > + case DP_AUX_NATIVE_REPLY_NACK: > + return -EIO; > + > + case DP_AUX_NATIVE_REPLY_DEFER: > + usleep_range(400, 500); > + break; > + } > + } > + > + DRM_ERROR("too many retries, giving up\n"); > + return -EIO; > +} > +EXPORT_SYMBOL(drm_dp_dpcd_write); > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index c873f9ce5871..0b14e799cd98 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -397,4 +397,68 @@ drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) > (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); > } > > +/* > + * DisplayPort AUX channel > + */ > + > +/** > + * struct drm_dp_aux_msg - DisplayPort AUX channel transaction > + * @address: address of the (first) register to access > + * @flags: contains the type of transaction as well as flags (see above) > + * @reply: upon completion, contains the reply type of the transaction > + * @buffer: pointer to a transmission or reception buffer > + * @size: size of @buffer > + */ > +struct drm_dp_aux_msg { > + unsigned int address; > + u8 request; > + u8 reply; > + void *buffer; > + size_t size; > +}; > + > +/** > + * struct drm_dp_aux - DisplayPort AUX channel > + * @transfer: transfers a message representing a single AUX transaction > + */ > +struct drm_dp_aux { > + ssize_t (*transfer)(struct drm_dp_aux *aux, > + struct drm_dp_aux_msg *msg); > +}; > + > +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, > + void *buffer, size_t size); > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, > + void *buffer, size_t size); > + > +/** > + * drm_dp_dpcd_readb() - read a single byte from the DPCD > + * @aux: DisplayPort AUX channel > + * @offset: address of the register to read > + * @valuep: location where the value of the register will be stored > + * > + * Returns the number of bytes transferred (1) on success, or a negative > + * error code on failure. > + */ > +static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux, > + unsigned int offset, u8 *valuep) > +{ > + return drm_dp_dpcd_read(aux, offset, valuep, 1); > +} > + > +/** > + * drm_dp_dpcd_writeb() - write a single byte to the DPCD > + * @aux: DisplayPort AUX channel > + * @offset: address of the register to write > + * @valuep: value to write to the register > + * > + * Returns the number of bytes transferred (1) on success, or a negative > + * error code on failure. > + */ > +static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, u8 value, > + unsigned int offset) > +{ > + return drm_dp_dpcd_write(aux, offset, &value, 1); > +} > + > #endif /* _DRM_DP_HELPER_H_ */ > -- > 1.8.4.2 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Tue, Dec 17, 2013 at 06:44:30PM +0200, Jani Nikula wrote: > On Tue, 17 Dec 2013, Thierry Reding <thierry.reding@gmail.com> wrote: [...] > > +/** > > + * 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. > > + */ > > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, > > + void *buffer, size_t size) [...] > > + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { > > + case DP_AUX_NATIVE_REPLY_ACK: > > + return 0; > > "Returns the number of bytes transferred on success, or a negative error > code on failure." Compare drm_dp_dpcd_read. Good catch! > You could add an internal helper to do both read and write, it's mostly > the same code. Yes, I've factored out drm_dp_dpcd_access() which takes an additional request parameter. That's the only difference between both functions. Thanks, Thierry
On Tue, 17 Dec 2013, Thierry Reding <thierry.reding@gmail.com> wrote: > This is a superset of the current i2c_dp_aux bus functionality and can > be used to transfer native AUX in addition to I2C-over-AUX messages. > > Helpers are provided to read and write the DPCD, either blockwise or > byte-wise. Many of the existing helpers for DisplayPort take a copy of a > portion of the DPCD and operate on that, without a way to write data > back to the DPCD (e.g. for configuration of the link). > > Subsequent patches will build upon this infrastructure to provide common > functionality in a generic way. > > Signed-off-by: Thierry Reding <treding@nvidia.com> > --- > drivers/gpu/drm/drm_dp_helper.c | 116 ++++++++++++++++++++++++++++++++++++++++ > include/drm/drm_dp_helper.h | 64 ++++++++++++++++++++++ > 2 files changed, 180 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c > index 9e978aae8972..bd622d462ca7 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -346,3 +346,119 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw) > } > } > EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); > + > +/** > + * 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. > + */ > +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_READ; > + 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++) { > + err = aux->transfer(aux, &msg); > + if (err < 0) { > + if (err == -EBUSY) > + continue; > + > + return err; > + } > + > + if (err == 0) > + return -EPROTO; > + > + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { > + case DP_AUX_NATIVE_REPLY_ACK: > + return err; > + > + case DP_AUX_NATIVE_REPLY_NACK: > + return -EIO; > + > + case DP_AUX_NATIVE_REPLY_DEFER: > + usleep_range(400, 500); > + break; > + } > + } > + > + DRM_ERROR("too many retries, giving up\n"); > + return -EIO; > +} > +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. > + */ > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_WRITE; > + 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++) { > + err = aux->transfer(aux, &msg); > + if (err < 0) { > + if (err == -EBUSY) > + continue; > + > + return err; > + } > + > + if (err == 0) > + return -EPROTO; > + > + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { > + case DP_AUX_NATIVE_REPLY_ACK: > + return 0; > + > + case DP_AUX_NATIVE_REPLY_NACK: > + return -EIO; > + > + case DP_AUX_NATIVE_REPLY_DEFER: > + usleep_range(400, 500); > + break; > + } > + } > + > + DRM_ERROR("too many retries, giving up\n"); > + return -EIO; > +} > +EXPORT_SYMBOL(drm_dp_dpcd_write); > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index c873f9ce5871..0b14e799cd98 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -397,4 +397,68 @@ drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) > (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); > } > > +/* > + * DisplayPort AUX channel > + */ > + > +/** > + * struct drm_dp_aux_msg - DisplayPort AUX channel transaction > + * @address: address of the (first) register to access > + * @flags: contains the type of transaction as well as flags (see above) > + * @reply: upon completion, contains the reply type of the transaction > + * @buffer: pointer to a transmission or reception buffer > + * @size: size of @buffer > + */ > +struct drm_dp_aux_msg { > + unsigned int address; > + u8 request; > + u8 reply; > + void *buffer; > + size_t size; > +}; > + > +/** > + * struct drm_dp_aux - DisplayPort AUX channel > + * @transfer: transfers a message representing a single AUX transaction > + */ > +struct drm_dp_aux { > + ssize_t (*transfer)(struct drm_dp_aux *aux, > + struct drm_dp_aux_msg *msg); > +}; > + > +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, > + void *buffer, size_t size); > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, > + void *buffer, size_t size); > + > +/** > + * drm_dp_dpcd_readb() - read a single byte from the DPCD > + * @aux: DisplayPort AUX channel > + * @offset: address of the register to read > + * @valuep: location where the value of the register will be stored > + * > + * Returns the number of bytes transferred (1) on success, or a negative > + * error code on failure. > + */ > +static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux, > + unsigned int offset, u8 *valuep) > +{ > + return drm_dp_dpcd_read(aux, offset, valuep, 1); > +} > + > +/** > + * drm_dp_dpcd_writeb() - write a single byte to the DPCD > + * @aux: DisplayPort AUX channel > + * @offset: address of the register to write > + * @valuep: value to write to the register > + * > + * Returns the number of bytes transferred (1) on success, or a negative > + * error code on failure. > + */ > +static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, u8 value, > + unsigned int offset) I'd much prefer offset before value parameter. Why would you have these the other way around here than in all the other functions? BR, Jani. > +{ > + return drm_dp_dpcd_write(aux, offset, &value, 1); > +} > + > #endif /* _DRM_DP_HELPER_H_ */ > -- > 1.8.4.2 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Fri, Dec 20, 2013 at 03:08:21PM +0200, Jani Nikula wrote: > On Tue, 17 Dec 2013, Thierry Reding <thierry.reding@gmail.com> wrote: [...] > > +/** > > + * drm_dp_dpcd_writeb() - write a single byte to the DPCD > > + * @aux: DisplayPort AUX channel > > + * @offset: address of the register to write > > + * @valuep: value to write to the register > > + * > > + * Returns the number of bytes transferred (1) on success, or a negative > > + * error code on failure. > > + */ > > +static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, u8 value, > > + unsigned int offset) > > I'd much prefer offset before value parameter. Why would you have these > the other way around here than in all the other functions? I guess I thought this would mirror the convention seen with readl() and writel(), but I suppose since drm_dp_dpcd_readb() doesn't actually look anything like readl() there's no consistency here anyway. I'll change it around. Thierry
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 9e978aae8972..bd622d462ca7 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -346,3 +346,119 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw) } } EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); + +/** + * 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. + */ +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_READ; + 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++) { + err = aux->transfer(aux, &msg); + if (err < 0) { + if (err == -EBUSY) + continue; + + return err; + } + + if (err == 0) + return -EPROTO; + + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { + case DP_AUX_NATIVE_REPLY_ACK: + return err; + + case DP_AUX_NATIVE_REPLY_NACK: + return -EIO; + + case DP_AUX_NATIVE_REPLY_DEFER: + usleep_range(400, 500); + break; + } + } + + DRM_ERROR("too many retries, giving up\n"); + return -EIO; +} +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. + */ +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, 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 = DP_AUX_NATIVE_WRITE; + 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++) { + err = aux->transfer(aux, &msg); + if (err < 0) { + if (err == -EBUSY) + continue; + + return err; + } + + if (err == 0) + return -EPROTO; + + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { + case DP_AUX_NATIVE_REPLY_ACK: + return 0; + + case DP_AUX_NATIVE_REPLY_NACK: + return -EIO; + + case DP_AUX_NATIVE_REPLY_DEFER: + usleep_range(400, 500); + break; + } + } + + DRM_ERROR("too many retries, giving up\n"); + return -EIO; +} +EXPORT_SYMBOL(drm_dp_dpcd_write); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c873f9ce5871..0b14e799cd98 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -397,4 +397,68 @@ drm_dp_enhanced_frame_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) (dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP); } +/* + * DisplayPort AUX channel + */ + +/** + * struct drm_dp_aux_msg - DisplayPort AUX channel transaction + * @address: address of the (first) register to access + * @flags: contains the type of transaction as well as flags (see above) + * @reply: upon completion, contains the reply type of the transaction + * @buffer: pointer to a transmission or reception buffer + * @size: size of @buffer + */ +struct drm_dp_aux_msg { + unsigned int address; + u8 request; + u8 reply; + void *buffer; + size_t size; +}; + +/** + * struct drm_dp_aux - DisplayPort AUX channel + * @transfer: transfers a message representing a single AUX transaction + */ +struct drm_dp_aux { + ssize_t (*transfer)(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg); +}; + +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size); +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size); + +/** + * drm_dp_dpcd_readb() - read a single byte from the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to read + * @valuep: location where the value of the register will be stored + * + * Returns the number of bytes transferred (1) on success, or a negative + * error code on failure. + */ +static inline ssize_t drm_dp_dpcd_readb(struct drm_dp_aux *aux, + unsigned int offset, u8 *valuep) +{ + return drm_dp_dpcd_read(aux, offset, valuep, 1); +} + +/** + * drm_dp_dpcd_writeb() - write a single byte to the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the register to write + * @valuep: value to write to the register + * + * Returns the number of bytes transferred (1) on success, or a negative + * error code on failure. + */ +static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, u8 value, + unsigned int offset) +{ + return drm_dp_dpcd_write(aux, offset, &value, 1); +} + #endif /* _DRM_DP_HELPER_H_ */
This is a superset of the current i2c_dp_aux bus functionality and can be used to transfer native AUX in addition to I2C-over-AUX messages. Helpers are provided to read and write the DPCD, either blockwise or byte-wise. Many of the existing helpers for DisplayPort take a copy of a portion of the DPCD and operate on that, without a way to write data back to the DPCD (e.g. for configuration of the link). Subsequent patches will build upon this infrastructure to provide common functionality in a generic way. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/gpu/drm/drm_dp_helper.c | 116 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 64 ++++++++++++++++++++++ 2 files changed, 180 insertions(+)