Skip to content

Commit

Permalink
drm: xlnx: zynqmp_dpsub: Anounce supported input formats
Browse files Browse the repository at this point in the history
DPSUB in bridge mode supports multiple input media bus formats.

Announce the list of supported input media bus formats via
drm_bridge.atomic_get_input_bus_fmts callback. Introduce a set of live
input formats supported by DPSUB. Add safeguards to format list functions
to prevent their misuse in the different layer modes contexts.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Anatoliy Klymenko <anatoliy.klymenko@amd.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240416-dp-live-fmt-v4-4-c7f379b7168e@amd.com
  • Loading branch information
Anatoliy Klymenko authored and Tomi Valkeinen committed Apr 24, 2024
1 parent 2e03666 commit b0f0469
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 4 deletions.
110 changes: 106 additions & 4 deletions drivers/gpu/drm/xlnx/zynqmp_disp.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/dma/xilinx_dpdma.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
Expand Down Expand Up @@ -77,12 +78,14 @@ enum zynqmp_dpsub_layer_mode {
/**
* struct zynqmp_disp_format - Display subsystem format information
* @drm_fmt: DRM format (4CC)
* @bus_fmt: Media bus format
* @buf_fmt: AV buffer format
* @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats
* @sf: Scaling factors for color components
*/
struct zynqmp_disp_format {
u32 drm_fmt;
u32 bus_fmt;
u32 buf_fmt;
bool swap;
const u32 *sf;
Expand Down Expand Up @@ -182,6 +185,12 @@ static const u32 scaling_factors_565[] = {
ZYNQMP_DISP_AV_BUF_5BIT_SF,
};

static const u32 scaling_factors_666[] = {
ZYNQMP_DISP_AV_BUF_6BIT_SF,
ZYNQMP_DISP_AV_BUF_6BIT_SF,
ZYNQMP_DISP_AV_BUF_6BIT_SF,
};

static const u32 scaling_factors_888[] = {
ZYNQMP_DISP_AV_BUF_8BIT_SF,
ZYNQMP_DISP_AV_BUF_8BIT_SF,
Expand Down Expand Up @@ -364,6 +373,41 @@ static const struct zynqmp_disp_format avbuf_gfx_fmts[] = {
},
};

/* List of live video layer formats */
static const struct zynqmp_disp_format avbuf_live_fmts[] = {
{
.drm_fmt = DRM_FORMAT_RGB565,
.bus_fmt = MEDIA_BUS_FMT_RGB666_1X18,
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_6 |
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB,
.sf = scaling_factors_666,
}, {
.drm_fmt = DRM_FORMAT_RGB888,
.bus_fmt = MEDIA_BUS_FMT_RGB888_1X24,
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_RGB,
.sf = scaling_factors_888,
}, {
.drm_fmt = DRM_FORMAT_YUV422,
.bus_fmt = MEDIA_BUS_FMT_UYVY8_1X16,
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422,
.sf = scaling_factors_888,
}, {
.drm_fmt = DRM_FORMAT_YUV444,
.bus_fmt = MEDIA_BUS_FMT_VUY8_1X24,
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_8 |
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV444,
.sf = scaling_factors_888,
}, {
.drm_fmt = DRM_FORMAT_P210,
.bus_fmt = MEDIA_BUS_FMT_UYVY10_1X20,
.buf_fmt = ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_BPC_10 |
ZYNQMP_DISP_AV_BUF_LIVE_CONFIG_FMT_YUV422,
.sf = scaling_factors_101010,
},
};

static u32 zynqmp_disp_avbuf_read(struct zynqmp_disp *disp, int reg)
{
return readl(disp->avbuf.base + reg);
Expand Down Expand Up @@ -887,6 +931,11 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
* @layer: The layer
* @num_formats: Pointer to the returned number of formats
*
* NOTE: This function doesn't make sense for live video layers and will
* always return an empty list in such cases. zynqmp_disp_live_layer_formats()
* should be used to query a list of media bus formats supported by the live
* video input layer.
*
* Return: A newly allocated u32 array that stores all the DRM formats
* supported by the layer. The number of formats in the array is returned
* through the num_formats argument.
Expand All @@ -897,10 +946,17 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
unsigned int i;
u32 *formats;

if (WARN_ON(!layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)) {
*num_formats = 0;
return NULL;
}

formats = kcalloc(layer->info->num_formats, sizeof(*formats),
GFP_KERNEL);
if (!formats)
if (!formats) {
*num_formats = 0;
return NULL;
}

for (i = 0; i < layer->info->num_formats; ++i)
formats[i] = layer->info->formats[i].drm_fmt;
Expand All @@ -909,6 +965,43 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
return formats;
}

/**
* zynqmp_disp_live_layer_formats - Return the media bus formats supported by
* the live video layer
* @layer: The layer
* @num_formats: Pointer to the returned number of formats
*
* NOTE: This function should be used only for live video input layers.
*
* Return: A newly allocated u32 array of media bus formats supported by the
* layer. The number of formats in the array is returned through the
* @num_formats argument.
*/
u32 *zynqmp_disp_live_layer_formats(struct zynqmp_disp_layer *layer,
unsigned int *num_formats)
{
unsigned int i;
u32 *formats;

if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_LIVE)) {
*num_formats = 0;
return NULL;
}

formats = kcalloc(layer->info->num_formats, sizeof(*formats),
GFP_KERNEL);
if (!formats) {
*num_formats = 0;
return NULL;
}

for (i = 0; i < layer->info->num_formats; ++i)
formats[i] = layer->info->formats[i].bus_fmt;

*num_formats = layer->info->num_formats;
return formats;
}

/**
* zynqmp_disp_layer_enable - Enable a layer
* @layer: The layer
Expand Down Expand Up @@ -1131,6 +1224,11 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
.num_channels = 1,
},
};
static const struct zynqmp_disp_layer_info live_layer_info = {
.formats = avbuf_live_fmts,
.num_formats = ARRAY_SIZE(avbuf_live_fmts),
.num_channels = 0,
};

unsigned int i;
int ret;
Expand All @@ -1140,13 +1238,17 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)

layer->id = i;
layer->disp = disp;
layer->info = &layer_info[i];
/*
* For now assume dpsub works in either live or non-live mode for both layers.
* Hybrid mode is not supported yet.
*/
layer->mode = disp->dpsub->dma_enabled ? ZYNQMP_DPSUB_LAYER_NONLIVE
: ZYNQMP_DPSUB_LAYER_LIVE;
if (disp->dpsub->dma_enabled) {
layer->mode = ZYNQMP_DPSUB_LAYER_NONLIVE;
layer->info = &layer_info[i];
} else {
layer->mode = ZYNQMP_DPSUB_LAYER_LIVE;
layer->info = &live_layer_info;
}

ret = zynqmp_disp_layer_request_dma(disp, layer);
if (ret)
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/xlnx/zynqmp_disp.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,

u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
unsigned int *num_formats);
u32 *zynqmp_disp_live_layer_formats(struct zynqmp_disp_layer *layer,
unsigned int *num_formats);
void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer);
void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer);
void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
Expand Down
31 changes: 31 additions & 0 deletions drivers/gpu/drm/xlnx/zynqmp_dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/media-bus-format.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
Expand Down Expand Up @@ -1577,6 +1578,35 @@ static const struct drm_edid *zynqmp_dp_bridge_edid_read(struct drm_bridge *brid
return drm_edid_read_ddc(connector, &dp->aux.ddc);
}

static u32 *zynqmp_dp_bridge_default_bus_fmts(unsigned int *num_input_fmts)
{
u32 *formats = kzalloc(sizeof(*formats), GFP_KERNEL);

if (formats)
*formats = MEDIA_BUS_FMT_FIXED;
*num_input_fmts = !!formats;

return formats;
}

static u32 *
zynqmp_dp_bridge_get_input_bus_fmts(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state,
u32 output_fmt,
unsigned int *num_input_fmts)
{
struct zynqmp_dp *dp = bridge_to_dp(bridge);
struct zynqmp_disp_layer *layer;

layer = zynqmp_dp_disp_connected_live_layer(dp);
if (layer)
return zynqmp_disp_live_layer_formats(layer, num_input_fmts);
else
return zynqmp_dp_bridge_default_bus_fmts(num_input_fmts);
}

static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
.attach = zynqmp_dp_bridge_attach,
.detach = zynqmp_dp_bridge_detach,
Expand All @@ -1589,6 +1619,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
.atomic_check = zynqmp_dp_bridge_atomic_check,
.detect = zynqmp_dp_bridge_detect,
.edid_read = zynqmp_dp_bridge_edid_read,
.atomic_get_input_bus_fmts = zynqmp_dp_bridge_get_input_bus_fmts,
};

/* -----------------------------------------------------------------------------
Expand Down

0 comments on commit b0f0469

Please sign in to comment.