modetest: add support for writeback connector

Add writeback support to modetest with the below options:

- Passing in -a -c will now also show the writeback connector

- Dump the writeback output buffer to bitstream
  Usage: "./modetest -M msm -s <connector_id>:<widthxheight>
          -a -o <filepath>
          -P <plane_id>@<crtc_id>:<widthxheight>+0+0@RG24"

This currently supports single writeback connector.

Co-developed-by: Rohith Iyer <quic_rohiiyer@quicinc.com>
Signed-off-by: Jessica Zhang <quic_jesszhan@quicinc.com>
[DB: dropped custom mode support, fixed segfault]
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
main
Rohith Iyer 2022-07-25 10:11:41 -07:00 committed by Dmitry Baryshkov
parent ee52a88a57
commit 8db39ef7bb
3 changed files with 146 additions and 3 deletions

View File

@ -338,3 +338,22 @@ void bo_destroy(struct bo *bo)
free(bo); free(bo);
} }
void bo_dump(struct bo *bo, const char *filename)
{
FILE *fp;
if (!bo || !filename)
return;
fp = fopen(filename, "wb");
if (fp) {
void *addr;
bo_map(bo, &addr);
printf("Dumping buffer %p to file %s.\n", bo->ptr, filename);
fwrite(bo->ptr, 1, bo->size, fp);
bo_unmap(bo);
fclose(fp);
}
}

View File

@ -36,5 +36,6 @@ struct bo *bo_create(int fd, unsigned int format,
unsigned int handles[4], unsigned int pitches[4], unsigned int handles[4], unsigned int pitches[4],
unsigned int offsets[4], enum util_fill_pattern pattern); unsigned int offsets[4], enum util_fill_pattern pattern);
void bo_destroy(struct bo *bo); void bo_destroy(struct bo *bo);
void bo_dump(struct bo *bo, const char *filename);
#endif #endif

View File

@ -128,6 +128,7 @@ struct device {
int use_atomic; int use_atomic;
drmModeAtomicReq *req; drmModeAtomicReq *req;
int32_t writeback_fence_fd;
}; };
static inline int64_t U642I64(uint64_t val) static inline int64_t U642I64(uint64_t val)
@ -811,6 +812,8 @@ struct pipe_arg {
struct crtc *crtc; struct crtc *crtc;
unsigned int fb_id[2], current_fb_id; unsigned int fb_id[2], current_fb_id;
struct timeval start; struct timeval start;
unsigned int out_fb_id;
struct bo *out_bo;
int swap_count; int swap_count;
}; };
@ -1441,6 +1444,24 @@ static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
return 0; return 0;
} }
static bool pipe_has_writeback_connector(struct device *dev, struct pipe_arg *pipes,
unsigned int count)
{
drmModeConnector *connector;
unsigned int i, j;
for (j = 0; j < count; j++) {
struct pipe_arg *pipe = &pipes[j];
for (i = 0; i < pipe->num_cons; i++) {
connector = get_connector_by_id(dev, pipe->con_ids[i]);
if (connector && connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
return true;
}
}
return false;
}
static int pipe_attempt_connector(struct device *dev, drmModeConnector *con, static int pipe_attempt_connector(struct device *dev, drmModeConnector *con,
struct pipe_arg *pipe) struct pipe_arg *pipe)
{ {
@ -1503,7 +1524,8 @@ static int pipe_find_preferred(struct device *dev, struct pipe_arg **out_pipes)
for (i = 0; i < res->count_connectors; i++) { for (i = 0; i < res->count_connectors; i++) {
con = res->connectors[i].connector; con = res->connectors[i].connector;
if (!con || con->connection != DRM_MODE_CONNECTED) if (!con || con->connection != DRM_MODE_CONNECTED ||
con->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
continue; continue;
connected++; connected++;
} }
@ -1662,6 +1684,75 @@ static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int co
} }
} }
static void writeback_config(struct device *dev, struct pipe_arg *pipes, unsigned int count)
{
drmModeConnector *connector;
unsigned int i, j;
for (j = 0; j < count; j++) {
struct pipe_arg *pipe = &pipes[j];
for (i = 0; i < pipe->num_cons; i++) {
connector = get_connector_by_id(dev, pipe->con_ids[i]);
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) {
if (!pipe->mode) {
fprintf(stderr, "no mode for writeback\n");
return;
}
bo_fb_create(dev->fd, pipes[j].fourcc,
pipe->mode->hdisplay, pipe->mode->vdisplay,
UTIL_PATTERN_PLAIN,
&pipe->out_bo, &pipe->out_fb_id);
add_property(dev, pipe->con_ids[i], "WRITEBACK_FB_ID",
pipe->out_fb_id);
add_property(dev, pipe->con_ids[i], "WRITEBACK_OUT_FENCE_PTR",
(uintptr_t)(&dev->writeback_fence_fd));
}
}
}
}
static int poll_writeback_fence(int fd, int timeout)
{
struct pollfd fds = { fd, POLLIN };
int ret;
do {
ret = poll(&fds, 1, timeout);
if (ret > 0) {
if (fds.revents & (POLLERR | POLLNVAL))
return -EINVAL;
return 0;
} else if (ret == 0) {
return -ETIMEDOUT;
} else {
ret = -errno;
if (ret == -EINTR || ret == -EAGAIN)
continue;
return ret;
}
} while (1);
}
static void dump_output_fb(struct device *dev, struct pipe_arg *pipes, char *dump_path,
unsigned int count)
{
drmModeConnector *connector;
unsigned int i, j;
for (j = 0; j < count; j++) {
struct pipe_arg *pipe = &pipes[j];
for (i = 0; i < pipe->num_cons; i++) {
connector = get_connector_by_id(dev, pipe->con_ids[i]);
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
bo_dump(pipe->out_bo, dump_path);
}
}
}
static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count) static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
{ {
unsigned int i; unsigned int i;
@ -1990,7 +2081,7 @@ static void parse_fill_patterns(char *arg)
static void usage(char *name) static void usage(char *name)
{ {
fprintf(stderr, "usage: %s [-acDdefMPpsCvrw]\n", name); fprintf(stderr, "usage: %s [-acDdefMoPpsCvrw]\n", name);
fprintf(stderr, "\n Query options:\n\n"); fprintf(stderr, "\n Query options:\n\n");
fprintf(stderr, "\t-c\tlist connectors\n"); fprintf(stderr, "\t-c\tlist connectors\n");
@ -2007,6 +2098,7 @@ static void usage(char *name)
fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n"); fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
fprintf(stderr, "\t-a \tuse atomic API\n"); fprintf(stderr, "\t-a \tuse atomic API\n");
fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n"); fprintf(stderr, "\t-F pattern1,pattern2\tspecify fill patterns\n");
fprintf(stderr, "\t-o <desired file path> \t Dump writeback output buffer to file\n");
fprintf(stderr, "\n Generic options:\n\n"); fprintf(stderr, "\n Generic options:\n\n");
fprintf(stderr, "\t-d\tdrop master after mode set\n"); fprintf(stderr, "\t-d\tdrop master after mode set\n");
@ -2017,7 +2109,7 @@ static void usage(char *name)
exit(0); exit(0);
} }
static char optstr[] = "acdD:efF:M:P:ps:Cvrw:"; static char optstr[] = "acdD:efF:M:P:ps:Cvrw:o:";
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -2040,6 +2132,7 @@ int main(int argc, char **argv)
struct property_arg *prop_args = NULL; struct property_arg *prop_args = NULL;
unsigned int args = 0; unsigned int args = 0;
int ret; int ret;
char *dump_path = NULL;
memset(&dev, 0, sizeof dev); memset(&dev, 0, sizeof dev);
@ -2078,6 +2171,9 @@ int main(int argc, char **argv)
/* Preserve the default behaviour of dumping all information. */ /* Preserve the default behaviour of dumping all information. */
args--; args--;
break; break;
case 'o':
dump_path = optarg;
break;
case 'P': case 'P':
plane_args = realloc(plane_args, plane_args = realloc(plane_args,
(plane_count + 1) * sizeof *plane_args); (plane_count + 1) * sizeof *plane_args);
@ -2163,6 +2259,7 @@ int main(int argc, char **argv)
if (use_atomic) { if (use_atomic) {
ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1); ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
drmSetClientCap(dev.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
if (ret) { if (ret) {
fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno)); fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
drmClose(dev.fd); drmClose(dev.fd);
@ -2204,6 +2301,15 @@ int main(int argc, char **argv)
if (set_preferred || count) if (set_preferred || count)
set_mode(&dev, pipe_args, count); set_mode(&dev, pipe_args, count);
if (dump_path) {
if (!pipe_has_writeback_connector(&dev, pipe_args, count)) {
fprintf(stderr, "No writeback connector found, can not dump.\n");
return 1;
}
writeback_config(&dev, pipe_args, count);
}
if (plane_count) if (plane_count)
atomic_set_planes(&dev, plane_args, plane_count, false); atomic_set_planes(&dev, plane_args, plane_count, false);
@ -2213,6 +2319,18 @@ int main(int argc, char **argv)
return 1; return 1;
} }
/*
* Since only writeback connectors have an output fb, this should only be
* called for writeback.
*/
if (dump_path) {
ret = poll_writeback_fence(dev.writeback_fence_fd, 1000);
if (ret)
fprintf(stderr, "Poll for writeback error: %d. Skipping Dump.\n",
ret);
dump_output_fb(&dev, pipe_args, dump_path, count);
}
if (test_vsync) if (test_vsync)
atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count); atomic_test_page_flip(&dev, pipe_args, plane_args, plane_count);
@ -2241,6 +2359,11 @@ int main(int argc, char **argv)
drmModeAtomicFree(dev.req); drmModeAtomicFree(dev.req);
} else { } else {
if (dump_path) {
fprintf(stderr, "writeback / dump is only supported in atomic mode\n");
return 1;
}
if (set_preferred || count || plane_count) { if (set_preferred || count || plane_count) {
uint64_t cap = 0; uint64_t cap = 0;