/** * 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;
};
/** * struct zynqmp_disp_layer_dma - DMA channel for one data plane of a layer * @chan: DMA channel * @xt: Interleaved DMA descriptor template * @sgl: Data chunk for dma_interleaved_template
*/ struct zynqmp_disp_layer_dma { struct dma_chan *chan; struct dma_interleaved_template xt; struct data_chunk sgl;
};
/** * struct zynqmp_disp_layer_info - Static layer information * @formats: Array of supported formats * @num_formats: Number of formats in @formats array * @num_channels: Number of DMA channels
*/ struct zynqmp_disp_layer_info { conststruct zynqmp_disp_format *formats; unsignedint num_formats; unsignedint num_channels;
};
/** * struct zynqmp_disp_layer - Display layer * @id: Layer ID * @disp: Back pointer to struct zynqmp_disp * @info: Static layer information * @dmas: DMA channels * @disp_fmt: Current format information * @drm_fmt: Current DRM format information * @mode: Current operation mode
*/ struct zynqmp_disp_layer { enum zynqmp_dpsub_layer_id id; struct zynqmp_disp *disp; conststruct zynqmp_disp_layer_info *info;
/** * zynqmp_disp_avbuf_set_format - Set the input format for a layer * @disp: Display controller * @layer: The layer * @fmt: The format information * * Set the video buffer manager format for @layer to @fmt.
*/ staticvoid zynqmp_disp_avbuf_set_format(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer, conststruct zynqmp_disp_format *fmt)
{ unsignedint i;
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++) {
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_set_clocks_sources - Set the clocks sources * @disp: Display controller * @video_from_ps: True if the video clock originates from the PS * @audio_from_ps: True if the audio clock originates from the PS * @timings_internal: True if video timings are generated internally * * Set the source for the video and audio clocks, as well as for the video * timings. Clocks can originate from the PS or PL, and timings can be * generated internally or externally.
*/ staticvoid
zynqmp_disp_avbuf_set_clocks_sources(struct zynqmp_disp *disp, bool video_from_ps, bool audio_from_ps, bool timings_internal)
{
u32 val = 0;
if (video_from_ps)
val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_FROM_PS; if (audio_from_ps)
val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_AUD_FROM_PS; if (timings_internal)
val |= ZYNQMP_DISP_AV_BUF_CLK_SRC_VID_INTERNAL_TIMING;
for (i = 0; i < ZYNQMP_DISP_AV_BUF_NUM_BUFFERS; i++)
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_CHBUF(i),
ZYNQMP_DISP_AV_BUF_CHBUF_FLUSH);
}
/** * zynqmp_disp_avbuf_enable_audio - Enable audio * @disp: Display controller * * Enable all audio buffers with a non-live (memory) source.
*/ staticvoid zynqmp_disp_avbuf_enable_audio(struct zynqmp_disp *disp)
{
u32 val;
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MEM;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN;
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
}
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_MASK;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_AUD1_DISABLE;
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_AUD2_EN;
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
}
/** * zynqmp_disp_avbuf_enable_video - Enable a video layer * @disp: Display controller * @layer: The layer * * Enable the video/graphics buffer for @layer.
*/ staticvoid zynqmp_disp_avbuf_enable_video(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer)
{
u32 val;
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); if (zynqmp_disp_layer_is_video(layer)) {
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK; if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM; else
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE;
} else {
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM; else
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE;
}
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
}
/** * zynqmp_disp_avbuf_disable_video - Disable a video layer * @disp: Display controller * @layer: The layer * * Disable the video/graphics buffer for @layer.
*/ staticvoid zynqmp_disp_avbuf_disable_video(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer)
{
u32 val;
val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT); if (zynqmp_disp_layer_is_video(layer)) {
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_NONE;
} else {
val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK;
val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_DISABLE;
}
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_OUTPUT, val);
}
/** * zynqmp_disp_avbuf_enable - Enable the video pipe * @disp: Display controller * * De-assert the video pipe reset.
*/ staticvoid zynqmp_disp_avbuf_enable(struct zynqmp_disp *disp)
{
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG, 0);
}
/** * zynqmp_disp_avbuf_disable - Disable the video pipe * @disp: Display controller * * Assert the video pipe reset.
*/ staticvoid zynqmp_disp_avbuf_disable(struct zynqmp_disp *disp)
{
zynqmp_disp_avbuf_write(disp, ZYNQMP_DISP_AV_BUF_SRST_REG,
ZYNQMP_DISP_AV_BUF_SRST_REG_VID_RST);
}
/** * zynqmp_disp_blend_set_output_format - Set the output format of the blender * @disp: Display controller * @format: Output format * * Set the output format of the blender to @format.
*/ staticvoid zynqmp_disp_blend_set_output_format(struct zynqmp_disp *disp, enum zynqmp_dpsub_format format)
{ staticconstunsignedint blend_output_fmts[] = {
[ZYNQMP_DPSUB_FORMAT_RGB] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_RGB,
[ZYNQMP_DPSUB_FORMAT_YCRCB444] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR444,
[ZYNQMP_DPSUB_FORMAT_YCRCB422] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YCBCR422
| ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_EN_DOWNSAMPLE,
[ZYNQMP_DPSUB_FORMAT_YONLY] = ZYNQMP_DISP_V_BLEND_OUTPUT_VID_FMT_YONLY,
};
for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i++)
zynqmp_disp_blend_write(disp,
ZYNQMP_DISP_V_BLEND_RGB2YCBCR_COEFF(i),
coeffs[i]);
for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++)
zynqmp_disp_blend_write(disp,
ZYNQMP_DISP_V_BLEND_OUTCSC_OFFSET(i),
offsets[i]);
}
/** * zynqmp_disp_blend_set_bg_color - Set the background color * @disp: Display controller * @rcr: Red/Cr color component * @gy: Green/Y color component * @bcb: Blue/Cb color component * * Set the background color to (@rcr, @gy, @bcb), corresponding to the R, G and * B or Cr, Y and Cb components respectively depending on the selected output * format.
*/ staticvoid zynqmp_disp_blend_set_bg_color(struct zynqmp_disp *disp,
u32 rcr, u32 gy, u32 bcb)
{
zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_0, rcr);
zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_1, gy);
zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_BG_CLR_2, bcb);
}
/** * zynqmp_disp_blend_set_global_alpha - Configure global alpha blending * @disp: Display controller * @enable: True to enable global alpha blending * @alpha: Global alpha value (ignored if @enabled is false)
*/ void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp, bool enable, u32 alpha)
{
zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA,
ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) |
(enable ? ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_EN : 0));
}
/** * zynqmp_disp_blend_layer_set_csc - Configure colorspace conversion for layer * @disp: Display controller * @layer: The layer * @coeffs: Colorspace conversion matrix * @offsets: Colorspace conversion offsets * * Configure the input colorspace conversion matrix and offsets for the @layer. * Columns of the matrix are automatically swapped based on the input format to * handle RGB and YCrCb components permutations.
*/ staticvoid zynqmp_disp_blend_layer_set_csc(struct zynqmp_disp *disp, struct zynqmp_disp_layer *layer, const u16 *coeffs, const u32 *offsets)
{ unsignedint swap[3] = { 0, 1, 2 }; unsignedint reg; unsignedint i;
if (layer->disp_fmt->swap) { if (layer->drm_fmt->is_yuv) { /* Swap U and V. */
swap[1] = 2;
swap[2] = 1;
} else { /* Swap R and B. */
swap[0] = 2;
swap[2] = 0;
}
}
if (zynqmp_disp_layer_is_video(layer))
reg = ZYNQMP_DISP_V_BLEND_IN1CSC_COEFF(0); else
reg = ZYNQMP_DISP_V_BLEND_IN2CSC_COEFF(0);
for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_COEFF; i += 3, reg += 12) {
zynqmp_disp_blend_write(disp, reg + 0, coeffs[i + swap[0]]);
zynqmp_disp_blend_write(disp, reg + 4, coeffs[i + swap[1]]);
zynqmp_disp_blend_write(disp, reg + 8, coeffs[i + swap[2]]);
}
if (zynqmp_disp_layer_is_video(layer))
reg = ZYNQMP_DISP_V_BLEND_IN1CSC_OFFSET(0); else
reg = ZYNQMP_DISP_V_BLEND_IN2CSC_OFFSET(0);
for (i = 0; i < ZYNQMP_DISP_V_BLEND_NUM_OFFSET; i++)
zynqmp_disp_blend_write(disp, reg + i * 4, offsets[i]);
}
/** * zynqmp_disp_layer_find_format - Find format information for a DRM format * @layer: The layer * @drm_fmt: DRM format to search * * Search display subsystem format information corresponding to the given DRM * format @drm_fmt for the @layer, and return a pointer to the format * descriptor. * * Return: A pointer to the format descriptor if found, NULL otherwise
*/ staticconststruct zynqmp_disp_format *
zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
u32 drm_fmt)
{ unsignedint i;
for (i = 0; i < layer->info->num_formats; i++) { if (layer->info->formats[i].drm_fmt == drm_fmt) return &layer->info->formats[i];
}
return NULL;
}
/** * 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
*/ staticconststruct zynqmp_disp_format *
zynqmp_disp_layer_find_live_format(struct zynqmp_disp_layer *layer,
u32 media_bus_format)
{ unsignedint 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.
*/
u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer, unsignedint *num_formats)
{ unsignedint i;
u32 *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, unsignedint *num_formats)
{ unsignedint i;
u32 *formats;
/** * zynqmp_disp_layer_enable - Enable a layer * @layer: The 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)
{
zynqmp_disp_avbuf_enable_video(layer->disp, layer);
zynqmp_disp_blend_layer_enable(layer->disp, layer);
}
/** * zynqmp_disp_layer_disable - Disable the layer * @layer: The layer * * Disable the layer by stopping its DMA channels and disabling it in the * audio/video buffer manager and the blender.
*/ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
{ unsignedint i;
if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE) { for (i = 0; i < layer->drm_fmt->num_planes; i++)
dmaengine_terminate_sync(layer->dmas[i].chan);
}
/** * zynqmp_disp_layer_set_format - Set the layer format * @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, conststruct drm_format_info *info)
{ unsignedint 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;
/* * Set pconfig for each DMA channel to indicate they're part of a * video group.
*/ for (i = 0; i < info->num_planes; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; struct xilinx_dpdma_peripheral_config pconfig = {
.video_group = true,
}; struct dma_slave_config config = {
.direction = DMA_MEM_TO_DEV,
.peripheral_config = &pconfig,
.peripheral_size = sizeof(pconfig),
};
dmaengine_slave_config(dma->chan, &config);
}
}
/** * 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_layer_update - Update the layer framebuffer * @layer: The layer * @state: The plane state * * Update the framebuffer for the layer by issuing a new DMA engine transaction * for the new framebuffer. * * Return: 0 on success, or the DMA descriptor failure error otherwise
*/ int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer, struct drm_plane_state *state)
{ conststruct drm_format_info *info = layer->drm_fmt; unsignedint i;
if (layer->mode == ZYNQMP_DPSUB_LAYER_LIVE) return 0;
for (i = 0; i < info->num_planes; i++) { unsignedint width = state->crtc_w / (i ? info->hsub : 1); unsignedint height = state->crtc_h / (i ? info->vsub : 1); struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; struct dma_async_tx_descriptor *desc;
dma_addr_t dma_addr;
for (i = 0; i < ARRAY_SIZE(disp->layers); i++) { struct zynqmp_disp_layer *layer = &disp->layers[i];
layer->id = i;
layer->disp = disp; /* * 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) goto err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.