diff options
Diffstat (limited to 'drivers/gpu/drm/xlnx/zynqmp_disp.c')
-rw-r--r-- | drivers/gpu/drm/xlnx/zynqmp_disp.c | 312 |
1 files changed, 218 insertions, 94 deletions
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 407bc07cec69..80d1e499a18d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -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> @@ -65,14 +66,26 @@ #define ZYNQMP_DISP_MAX_NUM_SUB_PLANES 3 /** + * enum zynqmp_dpsub_layer_mode - Layer mode + * @ZYNQMP_DPSUB_LAYER_NONLIVE: non-live (memory) mode + * @ZYNQMP_DPSUB_LAYER_LIVE: live (stream) mode + */ +enum zynqmp_dpsub_layer_mode { + ZYNQMP_DPSUB_LAYER_NONLIVE, + ZYNQMP_DPSUB_LAYER_LIVE, +}; + +/** * 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; @@ -128,24 +141,16 @@ struct zynqmp_disp_layer { * struct zynqmp_disp - Display controller * @dev: Device structure * @dpsub: Display subsystem - * @blend.base: Register I/O base address for the blender - * @avbuf.base: Register I/O base address for the audio/video buffer manager - * @audio.base: Registers I/O base address for the audio mixer + * @blend: Register I/O base address for the blender + * @avbuf: Register I/O base address for the audio/video buffer manager * @layers: Layers (planes) */ struct zynqmp_disp { struct device *dev; struct zynqmp_dpsub *dpsub; - struct { - void __iomem *base; - } blend; - struct { - void __iomem *base; - } avbuf; - struct { - void __iomem *base; - } audio; + void __iomem *blend; + void __iomem *avbuf; struct zynqmp_disp_layer layers[ZYNQMP_DPSUB_NUM_LAYERS]; }; @@ -172,6 +177,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, @@ -354,14 +365,49 @@ 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); + return readl(disp->avbuf + reg); } static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val) { - writel(val, disp->avbuf.base + reg); + writel(val, disp->avbuf + reg); } static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer) @@ -382,19 +428,29 @@ static void zynqmp_disp_avbuf_set_format(struct zynqmp_disp *disp, const struct zynqmp_disp_format *fmt) { unsigned int i; - u32 val; - - val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT); - val &= zynqmp_disp_layer_is_video(layer) - ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK - : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; - val |= fmt->buf_fmt; - zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_FMT, val); + u32 val, reg; + + layer->disp_fmt = fmt; + if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) { + reg = ZYNQMP_DISP_AV_BUF_FMT; + val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_FMT); + val &= zynqmp_disp_layer_is_video(layer) + ? ~ZYNQMP_DISP_AV_BUF_FMT_NL_VID_MASK + : ~ZYNQMP_DISP_AV_BUF_FMT_NL_GFX_MASK; + val |= fmt->buf_fmt; + zynqmp_disp_avbuf_write(disp, reg, val); + } else { + reg = zynqmp_disp_layer_is_video(layer) + ? ZYNQMP_DISP_AV_BUF_LIVE_VID_CONFIG + : ZYNQMP_DISP_AV_BUF_LIVE_GFX_CONFIG; + val = fmt->buf_fmt; + zynqmp_disp_avbuf_write(disp, reg, val); + } for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_SF; i++) { - unsigned int reg = zynqmp_disp_layer_is_video(layer) - ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i) - : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i); + reg = zynqmp_disp_layer_is_video(layer) + ? ZYNQMP_DISP_AV_BUF_VID_COMP_SF(i) + : ZYNQMP_DISP_AV_BUF_GFX_COMP_SF(i); zynqmp_disp_avbuf_write(disp, reg, fmt->sf[i]); } @@ -587,7 +643,7 @@ static void zynqmp_disp_avbuf_disable(struct zynqmp_disp *disp) static void zynqmp_disp_blend_write(struct zynqmp_disp *disp, int reg, u32 val) { - writel(val, disp->blend.base + reg); + writel(val, disp->blend + reg); } /* @@ -808,42 +864,6 @@ static void zynqmp_disp_blend_layer_disable(struct zynqmp_disp *disp, } /* ----------------------------------------------------------------------------- - * Audio Mixer - */ - -static void zynqmp_disp_audio_write(struct zynqmp_disp *disp, int reg, u32 val) -{ - writel(val, disp->audio.base + reg); -} - -/** - * zynqmp_disp_audio_enable - Enable the audio mixer - * @disp: Display controller - * - * Enable the audio mixer by de-asserting the soft reset. The audio state is set to - * default values by the reset, set the default mixer volume explicitly. - */ -static void zynqmp_disp_audio_enable(struct zynqmp_disp *disp) -{ - /* Clear the audio soft reset register as it's an non-reset flop. */ - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, 0); - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_MIXER_VOLUME, - ZYNQMP_DISP_AUD_MIXER_VOLUME_NO_SCALE); -} - -/** - * zynqmp_disp_audio_disable - Disable the audio mixer - * @disp: Display controller - * - * Disable the audio mixer by asserting its soft reset. - */ -static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp) -{ - zynqmp_disp_audio_write(disp, ZYNQMP_DISP_AUD_SOFT_RESET, - ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST); -} - -/* ----------------------------------------------------------------------------- * ZynqMP Display Layer & DRM Plane */ @@ -873,10 +893,40 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer, } /** + * zynqmp_disp_layer_find_live_format - Find format information for given + * media bus format + * @layer: The layer + * @media_bus_format: Media bus format to search + * + * Search display subsystem format information corresponding to the given media + * bus format @media_bus_format for the @layer, and return a pointer to the + * format descriptor. + * + * Return: A pointer to the format descriptor if found, NULL otherwise + */ +static const struct zynqmp_disp_format * +zynqmp_disp_layer_find_live_format(struct zynqmp_disp_layer *layer, + u32 media_bus_format) +{ + unsigned int i; + + for (i = 0; i < layer->info->num_formats; i++) + if (layer->info->formats[i].bus_fmt == media_bus_format) + return &layer->info->formats[i]; + + return NULL; +} + +/** * zynqmp_disp_layer_drm_formats - Return the DRM formats supported by the 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. @@ -887,10 +937,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; @@ -900,17 +957,51 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, } /** + * 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 - * @mode: Operating mode of layer * * Enable the @layer in the audio/video buffer manager and the blender. DMA * channels are started separately by zynqmp_disp_layer_update(). */ -void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer, - enum zynqmp_dpsub_layer_mode mode) +void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer) { - layer->mode = mode; zynqmp_disp_avbuf_enable_video(layer->disp, layer); zynqmp_disp_blend_layer_enable(layer->disp, layer); } @@ -926,7 +1017,7 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) { unsigned int i; - if (layer->disp->dpsub->dma_enabled) { + if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) { for (i = 0; i < layer->drm_fmt->num_planes; i++) dmaengine_terminate_sync(layer->dmas[i].chan); } @@ -940,6 +1031,9 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer) * @layer: The layer * @info: The format info * + * NOTE: Use zynqmp_disp_layer_set_live_format() to set media bus format for + * live video layers. + * * Set the format for @layer to @info. The layer must be disabled. */ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, @@ -947,14 +1041,16 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, { unsigned int i; + if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_NONLIVE)) + return; + layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format); + if (WARN_ON(!layer->disp_fmt)) + return; layer->drm_fmt = info; zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); - if (!layer->disp->dpsub->dma_enabled) - return; - /* * Set pconfig for each DMA channel to indicate they're part of a * video group. @@ -975,6 +1071,32 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer, } /** + * zynqmp_disp_layer_set_live_format - Set the live video layer format + * @layer: The layer + * @media_bus_format: Media bus format to set + * + * NOTE: This function should not be used to set format for non-live video + * layer. Use zynqmp_disp_layer_set_format() instead. + * + * Set the display format for the live @layer. The layer must be disabled. + */ +void zynqmp_disp_layer_set_live_format(struct zynqmp_disp_layer *layer, + u32 media_bus_format) +{ + if (WARN_ON(layer->mode != ZYNQMP_DPSUB_LAYER_LIVE)) + return; + + layer->disp_fmt = zynqmp_disp_layer_find_live_format(layer, + media_bus_format); + if (WARN_ON(!layer->disp_fmt)) + return; + + zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt); + + layer->drm_fmt = drm_format_info(layer->disp_fmt->drm_fmt); +} + +/** * zynqmp_disp_layer_update - Update the layer framebuffer * @layer: The layer * @state: The plane state @@ -990,7 +1112,7 @@ int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, const struct drm_format_info *info = layer->drm_fmt; unsigned int i; - if (!layer->disp->dpsub->dma_enabled) + if (layer->mode == ZYNQMP_DPSUB_LAYER_LIVE) return 0; for (i = 0; i < info->num_planes; i++) { @@ -1040,7 +1162,7 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, { unsigned int i; - if (!layer->info || !disp->dpsub->dma_enabled) + if (!layer->info) return; for (i = 0; i < layer->info->num_channels; i++) { @@ -1083,9 +1205,6 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp, unsigned int i; int ret; - if (!disp->dpsub->dma_enabled) - return 0; - for (i = 0; i < layer->info->num_channels; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; char dma_channel_name[16]; @@ -1124,6 +1243,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; @@ -1133,7 +1257,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. + */ + 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) @@ -1166,11 +1300,9 @@ void zynqmp_disp_enable(struct zynqmp_disp *disp) /* Choose clock source based on the DT clock handle. */ zynqmp_disp_avbuf_set_clocks_sources(disp, disp->dpsub->vid_clk_from_ps, disp->dpsub->aud_clk_from_ps, - true); + disp->dpsub->vid_clk_from_ps); zynqmp_disp_avbuf_enable_channels(disp); zynqmp_disp_avbuf_enable_audio(disp); - - zynqmp_disp_audio_enable(disp); } /** @@ -1179,8 +1311,6 @@ void zynqmp_disp_enable(struct zynqmp_disp *disp) */ void zynqmp_disp_disable(struct zynqmp_disp *disp) { - zynqmp_disp_audio_disable(disp); - zynqmp_disp_avbuf_disable_audio(disp); zynqmp_disp_avbuf_disable_channels(disp); zynqmp_disp_avbuf_disable(disp); @@ -1237,21 +1367,15 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub) disp->dev = &pdev->dev; disp->dpsub = dpsub; - disp->blend.base = devm_platform_ioremap_resource_byname(pdev, "blend"); - if (IS_ERR(disp->blend.base)) { - ret = PTR_ERR(disp->blend.base); - goto error; - } - - disp->avbuf.base = devm_platform_ioremap_resource_byname(pdev, "av_buf"); - if (IS_ERR(disp->avbuf.base)) { - ret = PTR_ERR(disp->avbuf.base); + disp->blend = devm_platform_ioremap_resource_byname(pdev, "blend"); + if (IS_ERR(disp->blend)) { + ret = PTR_ERR(disp->blend); goto error; } - disp->audio.base = devm_platform_ioremap_resource_byname(pdev, "aud"); - if (IS_ERR(disp->audio.base)) { - ret = PTR_ERR(disp->audio.base); + disp->avbuf = devm_platform_ioremap_resource_byname(pdev, "av_buf"); + if (IS_ERR(disp->avbuf)) { + ret = PTR_ERR(disp->avbuf); goto error; } |