drm/dp: add helpers for capture of frame CRCs
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
This commit is contained in:
parent
4bb310fd9e
commit
79c1da7c3b
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -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);
|
||||
/**
|
||||
|
@ -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_ */
|
||||
|
|
Loading…
Reference in New Issue