Skip to content

Commit

Permalink
drm/dp: add helpers for capture of frame CRCs
Browse files Browse the repository at this point in the history
Adds helpers for starting and stopping capture of frame CRCs through the
DPCD. When capture is on, a worker waits for vblanks and retrieves the
frame CRC to put it in the queue on the CRTC that is using the
eDP connector, so it's passed to userspace.

v2: Reuse drm_crtc_wait_one_vblank
    Update locking, as drm_crtc_add_crc_entry now takes the lock

v3: Don't call wake_up_interruptible directly, that's now done in
    drm_crtc_add_crc_entry.

v4: Style fixes (Sean Paul)
    Reworked retry of CRC reads (Sean Paul)
    Flush worker after stopping CRC generationa (Sean Paul)

v5: Move back to make the retry explicitly once

v6: Set and use the drm_crtc backpointer (Sean Paul)

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Link: http://patchwork.freedesktop.org/patch/msgid/20170303133936.14964-3-tomeu.vizoso@collabora.com
  • Loading branch information
tomeuv authored and atseanpaul committed Mar 6, 2017
1 parent 4bb310f commit 79c1da7
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
126 changes: 126 additions & 0 deletions drivers/gpu/drm/drm_dp_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,78 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = {
.unlock_bus = unlock_bus,
};

static int drm_dp_aux_get_crc(struct drm_dp_aux *aux, u8 *crc)
{
u8 buf, count;
int ret;

ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
if (ret < 0)
return ret;

WARN_ON(!(buf & DP_TEST_SINK_START));

ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK_MISC, &buf);
if (ret < 0)
return ret;

count = buf & DP_TEST_COUNT_MASK;
if (count == aux->crc_count)
return -EAGAIN; /* No CRC yet */

aux->crc_count = count;

/*
* At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes
* per component (RGB or CrYCb).
*/
ret = drm_dp_dpcd_read(aux, DP_TEST_CRC_R_CR, crc, 6);
if (ret < 0)
return ret;

return 0;
}

static void drm_dp_aux_crc_work(struct work_struct *work)
{
struct drm_dp_aux *aux = container_of(work, struct drm_dp_aux,
crc_work);
struct drm_crtc *crtc;
u8 crc_bytes[6];
uint32_t crcs[3];
int ret;

if (WARN_ON(!aux->crtc))
return;

crtc = aux->crtc;
while (crtc->crc.opened) {
drm_crtc_wait_one_vblank(crtc);
if (!crtc->crc.opened)
break;

ret = drm_dp_aux_get_crc(aux, crc_bytes);
if (ret == -EAGAIN) {
usleep_range(1000, 2000);
ret = drm_dp_aux_get_crc(aux, crc_bytes);
}

if (ret == -EAGAIN) {
DRM_DEBUG_KMS("Get CRC failed after retrying: %d\n",
ret);
continue;
} else if (ret) {
DRM_DEBUG_KMS("Failed to get a CRC: %d\n", ret);
continue;
}

crcs[0] = crc_bytes[0] | crc_bytes[1] << 8;
crcs[1] = crc_bytes[2] | crc_bytes[3] << 8;
crcs[2] = crc_bytes[4] | crc_bytes[5] << 8;
drm_crtc_add_crc_entry(crtc, false, 0, crcs);
}
}

/**
* drm_dp_aux_init() - minimally initialise an aux channel
* @aux: DisplayPort AUX channel
Expand All @@ -993,6 +1065,7 @@ static const struct i2c_lock_operations drm_dp_i2c_lock_ops = {
void drm_dp_aux_init(struct drm_dp_aux *aux)
{
mutex_init(&aux->hw_mutex);
INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work);

aux->ddc.algo = &drm_dp_i2c_algo;
aux->ddc.algo_data = aux;
Expand Down Expand Up @@ -1081,3 +1154,56 @@ int drm_dp_psr_setup_time(const u8 psr_cap[EDP_PSR_RECEIVER_CAP_SIZE])
EXPORT_SYMBOL(drm_dp_psr_setup_time);

#undef PSR_SETUP_TIME

/**
* drm_dp_start_crc() - start capture of frame CRCs
* @aux: DisplayPort AUX channel
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc)
{
u8 buf;
int ret;

ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
if (ret < 0)
return ret;

ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf | DP_TEST_SINK_START);
if (ret < 0)
return ret;

aux->crc_count = 0;
aux->crtc = crtc;
schedule_work(&aux->crc_work);

return 0;
}
EXPORT_SYMBOL(drm_dp_start_crc);

/**
* drm_dp_stop_crc() - stop capture of frame CRCs
* @aux: DisplayPort AUX channel
*
* Returns 0 on success or a negative error code on failure.
*/
int drm_dp_stop_crc(struct drm_dp_aux *aux)
{
u8 buf;
int ret;

ret = drm_dp_dpcd_readb(aux, DP_TEST_SINK, &buf);
if (ret < 0)
return ret;

ret = drm_dp_dpcd_writeb(aux, DP_TEST_SINK, buf & ~DP_TEST_SINK_START);
if (ret < 0)
return ret;

flush_work(&aux->crc_work);
aux->crtc = NULL;

return 0;
}
EXPORT_SYMBOL(drm_dp_stop_crc);
7 changes: 7 additions & 0 deletions include/drm/drm_dp_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,8 @@ struct drm_dp_aux_msg {
* @dev: pointer to struct device that is the parent for this AUX channel
* @crtc: backpointer to the crtc that is currently using this AUX channel
* @hw_mutex: internal mutex used for locking transfers
* @crc_work: worker that captures CRCs for each frame
* @crc_count: counter of captured frame CRCs
* @transfer: transfers a message representing a single AUX transaction
*
* The .dev field should be set to a pointer to the device that implements
Expand Down Expand Up @@ -771,6 +773,8 @@ struct drm_dp_aux {
struct device *dev;
struct drm_crtc *crtc;
struct mutex hw_mutex;
struct work_struct crc_work;
u8 crc_count;
ssize_t (*transfer)(struct drm_dp_aux *aux,
struct drm_dp_aux_msg *msg);
/**
Expand Down Expand Up @@ -849,4 +853,7 @@ void drm_dp_aux_init(struct drm_dp_aux *aux);
int drm_dp_aux_register(struct drm_dp_aux *aux);
void drm_dp_aux_unregister(struct drm_dp_aux *aux);

int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc);
int drm_dp_stop_crc(struct drm_dp_aux *aux);

#endif /* _DRM_DP_HELPER_H_ */

0 comments on commit 79c1da7

Please sign in to comment.