diff --git a/Documentation/devicetree/bindings/gpu/st,stih4xx.txt b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt index 2d150c311a0539e0caa9ab302bf9943c18eb0fc1..c99eb34e640b6782c066758f20922b21be6f75f0 100644 --- a/Documentation/devicetree/bindings/gpu/st,stih4xx.txt +++ b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt @@ -68,7 +68,7 @@ STMicroelectronics stih4xx platforms number of clocks may depend of the SoC type. - clock-names: names of the clocks listed in clocks property in the same order. - - hdmi,hpd-gpio: gpio id to detect if an hdmi cable is plugged or not. + - ddc: phandle of an I2C controller used for DDC EDID probing sti-hda: Required properties: @@ -83,6 +83,22 @@ sti-hda: - clock-names: names of the clocks listed in clocks property in the same order. +sti-hqvdp: + must be a child of sti-display-subsystem + Required properties: + - compatible: "st,stih-hqvdp" + - reg: Physical base address of the IP registers and length of memory mapped region. + - clocks: from common clock binding: handle hardware IP needed clocks, the + number of clocks may depend of the SoC type. + See ../clocks/clock-bindings.txt for details. + - clock-names: names of the clocks listed in clocks property in the same + order. + - resets: resets to be used by the device + See ../reset/reset.txt for details. + - reset-names: names of the resets listed in resets property in the same + order. + - st,vtg: phandle on vtg main device node. + Example: / { @@ -173,7 +189,6 @@ Example: interrupt-names = "irq"; clock-names = "pix", "tmds", "phy", "audio"; clocks = <&clockgen_c_vcc CLK_S_PIX_HDMI>, <&clockgen_c_vcc CLK_S_TMDS_HDMI>, <&clockgen_c_vcc CLK_S_HDMI_REJECT_PLL>, <&clockgen_b1 CLK_S_PCM_0>; - hdmi,hpd-gpio = <&PIO2 5>; }; sti-hda@fe85a000 { @@ -184,6 +199,16 @@ Example: clocks = <&clockgen_c_vcc CLK_S_PIX_HD>, <&clockgen_c_vcc CLK_S_HDDAC>; }; }; + + sti-hqvdp@9c000000 { + compatible = "st,stih407-hqvdp"; + reg = <0x9C00000 0x100000>; + clock-names = "hqvdp", "pix_main"; + clocks = <&clk_s_c0_flexgen CLK_MAIN_DISP>, <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>; + reset-names = "hqvdp"; + resets = <&softreset STIH407_HDQVDP_SOFTRESET>; + st,vtg = <&vtg_main>; + }; }; ... }; diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig index ae8850f3e63bc4669e796650a1b76a9e9ff21a9f..d6d6b705b8c1c6652387c7d37cfc6b3bcdcbb5bf 100644 --- a/drivers/gpu/drm/sti/Kconfig +++ b/drivers/gpu/drm/sti/Kconfig @@ -5,6 +5,7 @@ config DRM_STI select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER + select FW_LOADER_USER_HELPER_FALLBACK help Choose this option to enable DRM on STM stiH41x chipset diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile index 04ac2ceef27fd640519e66aa22d6a3958a901ced..6ba9d27c1b90b81060754ee9893b539851044118 100644 --- a/drivers/gpu/drm/sti/Makefile +++ b/drivers/gpu/drm/sti/Makefile @@ -3,6 +3,7 @@ sticompositor-y := \ sti_mixer.o \ sti_gdp.o \ sti_vid.o \ + sti_cursor.o \ sti_compositor.o \ sti_drm_crtc.o \ sti_drm_plane.o @@ -18,4 +19,5 @@ obj-$(CONFIG_DRM_STI) = \ sti_hda.o \ sti_tvout.o \ sticompositor.o \ - sti_drm_drv.o \ No newline at end of file + sti_hqvdp.o \ + sti_drm_drv.o diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c index 390d93e9a06c18971175c0bd6164d05f2b0bdbed..c5cf4aea96944f5a147cc0ef68326ee6077c114a 100644 --- a/drivers/gpu/drm/sti/sti_compositor.c +++ b/drivers/gpu/drm/sti/sti_compositor.c @@ -24,14 +24,16 @@ * stiH407 compositor properties */ struct sti_compositor_data stih407_compositor_data = { - .nb_subdev = 6, + .nb_subdev = 8, .subdev_desc = { + {STI_CURSOR_SUBDEV, (int)STI_CURSOR, 0x000}, {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100}, {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200}, {STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300}, {STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400}, {STI_VID_SUBDEV, (int)STI_VID_0, 0x700}, - {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00} + {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}, + {STI_MIXER_AUX_SUBDEV, STI_MIXER_AUX, 0xD00}, }, }; @@ -67,11 +69,11 @@ static int sti_compositor_init_subdev(struct sti_compositor *compo, break; case STI_GPD_SUBDEV: case STI_VID_SUBDEV: + case STI_CURSOR_SUBDEV: compo->layer[layer_id++] = sti_layer_create(compo->dev, desc[i].id, compo->regs + desc[i].offset); break; - /* case STI_CURSOR_SUBDEV : TODO */ default: DRM_ERROR("Unknow subdev compoment type\n"); return 1; @@ -102,33 +104,35 @@ static int sti_compositor_bind(struct device *dev, struct device *master, enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK; enum drm_plane_type plane_type = DRM_PLANE_TYPE_OVERLAY; - if (compo->mixer[crtc]) + if (crtc < compo->nb_mixers) plane_type = DRM_PLANE_TYPE_PRIMARY; switch (type) { case STI_CUR: cursor = sti_drm_plane_init(drm_dev, compo->layer[i], - (1 << crtc) - 1, - DRM_PLANE_TYPE_CURSOR); + 1, DRM_PLANE_TYPE_CURSOR); break; case STI_GDP: case STI_VID: primary = sti_drm_plane_init(drm_dev, compo->layer[i], - (1 << crtc) - 1, plane_type); + (1 << compo->nb_mixers) - 1, + plane_type); plane++; break; case STI_BCK: + case STI_VDP: break; } /* The first planes are reserved for primary planes*/ - if (compo->mixer[crtc]) { + if (crtc < compo->nb_mixers && primary) { sti_drm_crtc_init(drm_dev, compo->mixer[crtc], primary, cursor); crtc++; cursor = NULL; + primary = NULL; } } } diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h index 3ea19db72e0f908feb6fe26b494699f4dabc2a82..019eb44c62cc4c51923dac410f67c158613ff6f4 100644 --- a/drivers/gpu/drm/sti/sti_compositor.h +++ b/drivers/gpu/drm/sti/sti_compositor.h @@ -64,7 +64,6 @@ struct sti_compositor_data { * @layer: array of layers * @nb_mixers: number of mixers for this compositor * @nb_layers: number of layers (GDP,VID,...) for this compositor - * @enable: true if compositor is enable else false * @vtg_vblank_nb: callback for VTG VSYNC notification */ struct sti_compositor { @@ -83,7 +82,6 @@ struct sti_compositor { struct sti_layer *layer[STI_MAX_LAYER]; int nb_mixers; int nb_layers; - bool enable; struct notifier_block vtg_vblank_nb; }; diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c new file mode 100644 index 0000000000000000000000000000000000000000..010eaee60bf7c17c27f5e8e3d14f76771e161ccd --- /dev/null +++ b/drivers/gpu/drm/sti/sti_cursor.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Vincent Abriou + * Fabien Dessenne + * for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ +#include + +#include "sti_cursor.h" +#include "sti_layer.h" +#include "sti_vtg.h" + +/* Registers */ +#define CUR_CTL 0x00 +#define CUR_VPO 0x0C +#define CUR_PML 0x14 +#define CUR_PMP 0x18 +#define CUR_SIZE 0x1C +#define CUR_CML 0x20 +#define CUR_AWS 0x28 +#define CUR_AWE 0x2C + +#define CUR_CTL_CLUT_UPDATE BIT(1) + +#define STI_CURS_MIN_SIZE 1 +#define STI_CURS_MAX_SIZE 128 + +/* + * pixmap dma buffer stucture + * + * @paddr: physical address + * @size: buffer size + * @base: virtual address + */ +struct dma_pixmap { + dma_addr_t paddr; + size_t size; + void *base; +}; + +/** + * STI Cursor structure + * + * @layer: layer structure + * @width: cursor width + * @height: cursor height + * @clut: color look up table + * @clut_paddr: color look up table physical address + * @pixmap: pixmap dma buffer (clut8-format cursor) + */ +struct sti_cursor { + struct sti_layer layer; + unsigned int width; + unsigned int height; + unsigned short *clut; + dma_addr_t clut_paddr; + struct dma_pixmap pixmap; +}; + +static const uint32_t cursor_supported_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +#define to_sti_cursor(x) container_of(x, struct sti_cursor, layer) + +static const uint32_t *sti_cursor_get_formats(struct sti_layer *layer) +{ + return cursor_supported_formats; +} + +static unsigned int sti_cursor_get_nb_formats(struct sti_layer *layer) +{ + return ARRAY_SIZE(cursor_supported_formats); +} + +static void sti_cursor_argb8888_to_clut8(struct sti_layer *layer) +{ + struct sti_cursor *cursor = to_sti_cursor(layer); + u32 *src = layer->vaddr; + u8 *dst = cursor->pixmap.base; + unsigned int i, j; + u32 a, r, g, b; + + for (i = 0; i < cursor->height; i++) { + for (j = 0; j < cursor->width; j++) { + /* Pick the 2 higher bits of each component */ + a = (*src >> 30) & 3; + r = (*src >> 22) & 3; + g = (*src >> 14) & 3; + b = (*src >> 6) & 3; + *dst = a << 6 | r << 4 | g << 2 | b; + src++; + dst++; + } + } +} + +static int sti_cursor_prepare_layer(struct sti_layer *layer, bool first_prepare) +{ + struct sti_cursor *cursor = to_sti_cursor(layer); + struct drm_display_mode *mode = layer->mode; + u32 y, x; + u32 val; + + DRM_DEBUG_DRIVER("\n"); + + dev_dbg(layer->dev, "%s %s\n", __func__, sti_layer_to_str(layer)); + + if (layer->src_w < STI_CURS_MIN_SIZE || + layer->src_h < STI_CURS_MIN_SIZE || + layer->src_w > STI_CURS_MAX_SIZE || + layer->src_h > STI_CURS_MAX_SIZE) { + DRM_ERROR("Invalid cursor size (%dx%d)\n", + layer->src_w, layer->src_h); + return -EINVAL; + } + + /* If the cursor size has changed, re-allocated the pixmap */ + if (!cursor->pixmap.base || + (cursor->width != layer->src_w) || + (cursor->height != layer->src_h)) { + cursor->width = layer->src_w; + cursor->height = layer->src_h; + + if (cursor->pixmap.base) + dma_free_writecombine(layer->dev, + cursor->pixmap.size, + cursor->pixmap.base, + cursor->pixmap.paddr); + + cursor->pixmap.size = cursor->width * cursor->height; + + cursor->pixmap.base = dma_alloc_writecombine(layer->dev, + cursor->pixmap.size, + &cursor->pixmap.paddr, + GFP_KERNEL | GFP_DMA); + if (!cursor->pixmap.base) { + DRM_ERROR("Failed to allocate memory for pixmap\n"); + return -ENOMEM; + } + } + + /* Convert ARGB8888 to CLUT8 */ + sti_cursor_argb8888_to_clut8(layer); + + /* AWS and AWE depend on the mode */ + y = sti_vtg_get_line_number(*mode, 0); + x = sti_vtg_get_pixel_number(*mode, 0); + val = y << 16 | x; + writel(val, layer->regs + CUR_AWS); + y = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); + x = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); + val = y << 16 | x; + writel(val, layer->regs + CUR_AWE); + + if (first_prepare) { + /* Set and fetch CLUT */ + writel(cursor->clut_paddr, layer->regs + CUR_CML); + writel(CUR_CTL_CLUT_UPDATE, layer->regs + CUR_CTL); + } + + return 0; +} + +static int sti_cursor_commit_layer(struct sti_layer *layer) +{ + struct sti_cursor *cursor = to_sti_cursor(layer); + struct drm_display_mode *mode = layer->mode; + u32 ydo, xdo; + + dev_dbg(layer->dev, "%s %s\n", __func__, sti_layer_to_str(layer)); + + /* Set memory location, size, and position */ + writel(cursor->pixmap.paddr, layer->regs + CUR_PML); + writel(cursor->width, layer->regs + CUR_PMP); + writel(cursor->height << 16 | cursor->width, layer->regs + CUR_SIZE); + + ydo = sti_vtg_get_line_number(*mode, layer->dst_y); + xdo = sti_vtg_get_pixel_number(*mode, layer->dst_y); + writel((ydo << 16) | xdo, layer->regs + CUR_VPO); + + return 0; +} + +static int sti_cursor_disable_layer(struct sti_layer *layer) +{ + return 0; +} + +static void sti_cursor_init(struct sti_layer *layer) +{ + struct sti_cursor *cursor = to_sti_cursor(layer); + unsigned short *base = cursor->clut; + unsigned int a, r, g, b; + + /* Assign CLUT values, ARGB444 format */ + for (a = 0; a < 4; a++) + for (r = 0; r < 4; r++) + for (g = 0; g < 4; g++) + for (b = 0; b < 4; b++) + *base++ = (a * 5) << 12 | + (r * 5) << 8 | + (g * 5) << 4 | + (b * 5); +} + +static const struct sti_layer_funcs cursor_ops = { + .get_formats = sti_cursor_get_formats, + .get_nb_formats = sti_cursor_get_nb_formats, + .init = sti_cursor_init, + .prepare = sti_cursor_prepare_layer, + .commit = sti_cursor_commit_layer, + .disable = sti_cursor_disable_layer, +}; + +struct sti_layer *sti_cursor_create(struct device *dev) +{ + struct sti_cursor *cursor; + + cursor = devm_kzalloc(dev, sizeof(*cursor), GFP_KERNEL); + if (!cursor) { + DRM_ERROR("Failed to allocate memory for cursor\n"); + return NULL; + } + + /* Allocate clut buffer */ + cursor->clut = dma_alloc_writecombine(dev, + 0x100 * sizeof(unsigned short), + &cursor->clut_paddr, + GFP_KERNEL | GFP_DMA); + + if (!cursor->clut) { + DRM_ERROR("Failed to allocate memory for cursor clut\n"); + devm_kfree(dev, cursor); + return NULL; + } + + cursor->layer.ops = &cursor_ops; + + return (struct sti_layer *)cursor; +} diff --git a/drivers/gpu/drm/sti/sti_cursor.h b/drivers/gpu/drm/sti/sti_cursor.h new file mode 100644 index 0000000000000000000000000000000000000000..3c9827404f27900e5239ae4140730da415514315 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_cursor.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) STMicroelectronics SA 2013 + * Authors: Vincent Abriou for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_CURSOR_H_ +#define _STI_CURSOR_H_ + +struct sti_layer *sti_cursor_create(struct device *dev); + +#endif diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c index 36a1ad3c482319e288c539bcbf7dfbe27a72074d..4c651c200f205693f9e166963803992008255ea5 100644 --- a/drivers/gpu/drm/sti/sti_drm_crtc.c +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c @@ -28,7 +28,7 @@ static void sti_drm_crtc_prepare(struct drm_crtc *crtc) struct device *dev = mixer->dev; struct sti_compositor *compo = dev_get_drvdata(dev); - compo->enable = true; + mixer->enabled = true; /* Prepare and enable the compo IP clock */ if (mixer->id == STI_MIXER_MAIN) { @@ -38,6 +38,8 @@ static void sti_drm_crtc_prepare(struct drm_crtc *crtc) if (clk_prepare_enable(compo->clk_compo_aux)) DRM_INFO("Failed to prepare/enable compo_aux clk\n"); } + + sti_mixer_clear_all_layers(mixer); } static void sti_drm_crtc_commit(struct drm_crtc *crtc) @@ -62,6 +64,8 @@ static void sti_drm_crtc_commit(struct drm_crtc *crtc) /* Enable layer on mixer */ if (sti_mixer_set_layer_status(mixer, layer, true)) DRM_ERROR("Can not enable layer at mixer\n"); + + drm_crtc_vblank_on(crtc); } static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc, @@ -144,7 +148,8 @@ sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, w = crtc->primary->fb->width - x; h = crtc->primary->fb->height - y; - return sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode, + return sti_layer_prepare(layer, crtc, + crtc->primary->fb, &crtc->mode, mixer->id, 0, 0, w, h, x, y, w, h); } @@ -171,7 +176,8 @@ static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, w = crtc->primary->fb->width - crtc->x; h = crtc->primary->fb->height - crtc->y; - ret = sti_layer_prepare(layer, crtc->primary->fb, &crtc->mode, + ret = sti_layer_prepare(layer, crtc, + crtc->primary->fb, &crtc->mode, mixer->id, 0, 0, w, h, crtc->x, crtc->y, w, h); if (ret) { @@ -196,7 +202,7 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) struct sti_compositor *compo = dev_get_drvdata(dev); struct sti_layer *layer; - if (!compo->enable) + if (!mixer->enabled) return; DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer)); @@ -222,7 +228,7 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) /* Then disable layer itself */ sti_layer_disable(layer); - drm_vblank_off(crtc->dev, mixer->id); + drm_crtc_vblank_off(crtc); /* Disable pixel clock and compo IP clocks */ if (mixer->id == STI_MIXER_MAIN) { @@ -233,7 +239,7 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc) clk_disable_unprepare(compo->clk_compo_aux); } - compo->enable = false; + mixer->enabled = false; } static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = { @@ -364,7 +370,6 @@ void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) struct sti_drm_private *priv = dev->dev_private; struct sti_compositor *compo = priv->compo; struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb; - unsigned long flags; DRM_DEBUG_DRIVER("\n"); @@ -373,13 +378,10 @@ void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); /* free the resources of the pending requests */ - spin_lock_irqsave(&dev->event_lock, flags); if (compo->mixer[crtc]->pending_event) { drm_vblank_put(dev, crtc); compo->mixer[crtc]->pending_event = NULL; } - spin_unlock_irqrestore(&dev->event_lock, flags); - } EXPORT_SYMBOL(sti_drm_crtc_disable_vblank); @@ -399,6 +401,7 @@ bool sti_drm_crtc_is_main(struct drm_crtc *crtc) return false; } +EXPORT_SYMBOL(sti_drm_crtc_is_main); int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer, struct drm_plane *primary, struct drm_plane *cursor) diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c index 223d93c3a05d8d7241ad05b669562d0a160af4fa..9ce7574ac8736385c2dcf123a4215cc3d270587c 100644 --- a/drivers/gpu/drm/sti/sti_drm_drv.c +++ b/drivers/gpu/drm/sti/sti_drm_drv.c @@ -67,8 +67,12 @@ static int sti_drm_load(struct drm_device *dev, unsigned long flags) sti_drm_mode_config_init(dev); ret = component_bind_all(dev->dev, dev); - if (ret) + if (ret) { + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + kfree(private); return ret; + } drm_helper_disable_unused_functions(dev); diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c index f4118d4cac22c6bb7f85abe791b5c9f93c2ae2ae..c9dd0e57cac19ac4b01f506cfb54ac96596264d1 100644 --- a/drivers/gpu/drm/sti/sti_drm_plane.c +++ b/drivers/gpu/drm/sti/sti_drm_plane.c @@ -45,7 +45,8 @@ sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, } /* src_x are in 16.16 format. */ - res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id, + res = sti_layer_prepare(layer, crtc, fb, + &crtc->mode, mixer->id, crtc_x, crtc_y, crtc_w, crtc_h, src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16); diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c index 4e30b74559f5cbdd2b88be9c90ceac5f7577ece5..32448d1d1e8f264c4cccd160efacdb89472f8b99 100644 --- a/drivers/gpu/drm/sti/sti_gdp.c +++ b/drivers/gpu/drm/sti/sti_gdp.c @@ -73,7 +73,9 @@ struct sti_gdp_node { struct sti_gdp_node_list { struct sti_gdp_node *top_field; + dma_addr_t top_field_paddr; struct sti_gdp_node *btm_field; + dma_addr_t btm_field_paddr; }; /** @@ -81,6 +83,8 @@ struct sti_gdp_node_list { * * @layer: layer structure * @clk_pix: pixel clock for the current gdp + * @clk_main_parent: gdp parent clock if main path used + * @clk_aux_parent: gdp parent clock if aux path used * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification * @is_curr_top: true if the current node processed is the top field * @node_list: array of node list @@ -88,6 +92,8 @@ struct sti_gdp_node_list { struct sti_gdp { struct sti_layer layer; struct clk *clk_pix; + struct clk *clk_main_parent; + struct clk *clk_aux_parent; struct notifier_block vtg_field_nb; bool is_curr_top; struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; @@ -168,7 +174,6 @@ static int sti_gdp_get_alpharange(int format) static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer) { int hw_nvn; - void *virt_nvn; struct sti_gdp *gdp = to_sti_gdp(layer); unsigned int i; @@ -176,11 +181,9 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer) if (!hw_nvn) goto end; - virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn); - for (i = 0; i < GDP_NODE_NB_BANK; i++) - if ((virt_nvn != gdp->node_list[i].btm_field) && - (virt_nvn != gdp->node_list[i].top_field)) + if ((hw_nvn != gdp->node_list[i].btm_field_paddr) && + (hw_nvn != gdp->node_list[i].top_field_paddr)) return &gdp->node_list[i]; /* in hazardious cases restart with the first node */ @@ -204,7 +207,6 @@ static struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer) { int hw_nvn; - void *virt_nvn; struct sti_gdp *gdp = to_sti_gdp(layer); unsigned int i; @@ -212,11 +214,9 @@ struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer) if (!hw_nvn) goto end; - virt_nvn = dma_to_virt(layer->dev, (dma_addr_t) hw_nvn); - for (i = 0; i < GDP_NODE_NB_BANK; i++) - if ((virt_nvn == gdp->node_list[i].btm_field) || - (virt_nvn == gdp->node_list[i].top_field)) + if ((hw_nvn == gdp->node_list[i].btm_field_paddr) || + (hw_nvn == gdp->node_list[i].top_field_paddr)) return &gdp->node_list[i]; end: @@ -292,8 +292,8 @@ static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) /* Same content and chained together */ memcpy(btm_field, top_field, sizeof(*btm_field)); - top_field->gam_gdp_nvn = virt_to_dma(dev, btm_field); - btm_field->gam_gdp_nvn = virt_to_dma(dev, top_field); + top_field->gam_gdp_nvn = list->btm_field_paddr; + btm_field->gam_gdp_nvn = list->top_field_paddr; /* Interlaced mode */ if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -311,6 +311,17 @@ static int sti_gdp_prepare_layer(struct sti_layer *layer, bool first_prepare) /* Set and enable gdp clock */ if (gdp->clk_pix) { + struct clk *clkp; + /* According to the mixer used, the gdp pixel clock + * should have a different parent clock. */ + if (layer->mixer_id == STI_MIXER_MAIN) + clkp = gdp->clk_main_parent; + else + clkp = gdp->clk_aux_parent; + + if (clkp) + clk_set_parent(gdp->clk_pix, clkp); + res = clk_set_rate(gdp->clk_pix, rate); if (res < 0) { DRM_ERROR("Cannot set rate (%dHz) for gdp\n", @@ -349,8 +360,8 @@ static int sti_gdp_commit_layer(struct sti_layer *layer) struct sti_gdp_node *updated_top_node = updated_list->top_field; struct sti_gdp_node *updated_btm_node = updated_list->btm_field; struct sti_gdp *gdp = to_sti_gdp(layer); - u32 dma_updated_top = virt_to_dma(layer->dev, updated_top_node); - u32 dma_updated_btm = virt_to_dma(layer->dev, updated_btm_node); + u32 dma_updated_top = updated_list->top_field_paddr; + u32 dma_updated_btm = updated_list->btm_field_paddr; struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer); dev_dbg(layer->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__, @@ -461,16 +472,16 @@ static void sti_gdp_init(struct sti_layer *layer) { struct sti_gdp *gdp = to_sti_gdp(layer); struct device_node *np = layer->dev->of_node; - dma_addr_t dma; + dma_addr_t dma_addr; void *base; unsigned int i, size; /* Allocate all the nodes within a single memory page */ size = sizeof(struct sti_gdp_node) * GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; - base = dma_alloc_writecombine(layer->dev, - size, &dma, GFP_KERNEL | GFP_DMA); + size, &dma_addr, GFP_KERNEL | GFP_DMA); + if (!base) { DRM_ERROR("Failed to allocate memory for GDP node\n"); return; @@ -478,21 +489,26 @@ static void sti_gdp_init(struct sti_layer *layer) memset(base, 0, size); for (i = 0; i < GDP_NODE_NB_BANK; i++) { - if (virt_to_dma(layer->dev, base) & 0xF) { + if (dma_addr & 0xF) { DRM_ERROR("Mem alignment failed\n"); return; } gdp->node_list[i].top_field = base; + gdp->node_list[i].top_field_paddr = dma_addr; + DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base); base += sizeof(struct sti_gdp_node); + dma_addr += sizeof(struct sti_gdp_node); - if (virt_to_dma(layer->dev, base) & 0xF) { + if (dma_addr & 0xF) { DRM_ERROR("Mem alignment failed\n"); return; } gdp->node_list[i].btm_field = base; + gdp->node_list[i].btm_field_paddr = dma_addr; DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base); base += sizeof(struct sti_gdp_node); + dma_addr += sizeof(struct sti_gdp_node); } if (of_device_is_compatible(np, "st,stih407-compositor")) { @@ -520,6 +536,14 @@ static void sti_gdp_init(struct sti_layer *layer) gdp->clk_pix = devm_clk_get(layer->dev, clk_name); if (IS_ERR(gdp->clk_pix)) DRM_ERROR("Cannot get %s clock\n", clk_name); + + gdp->clk_main_parent = devm_clk_get(layer->dev, "main_parent"); + if (IS_ERR(gdp->clk_main_parent)) + DRM_ERROR("Cannot get main_parent clock\n"); + + gdp->clk_aux_parent = devm_clk_get(layer->dev, "aux_parent"); + if (IS_ERR(gdp->clk_aux_parent)) + DRM_ERROR("Cannot get aux_parent clock\n"); } } diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index b22968c08d1fb7babe739939992a060eb9756cd3..d032e024b0b81ac7e68ccc48467b20750ec2c665 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -130,8 +130,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg) /* Hot plug/unplug IRQ */ if (hdmi->irq_status & HDMI_INT_HOT_PLUG) { - /* read gpio to get the status */ - hdmi->hpd = gpio_get_value(hdmi->hpd_gpio); + hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG; if (hdmi->drm_dev) drm_helper_hpd_irq_event(hdmi->drm_dev); } @@ -273,31 +272,32 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi) hdmi_write(hdmi, val, HDMI_SW_DI_CFG); /* Infoframe header */ - val = buffer[0x0]; - val |= buffer[0x1] << 8; - val |= buffer[0x2] << 16; + val = buffer[0]; + val |= buffer[1] << 8; + val |= buffer[2] << 16; hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI)); /* Infoframe packet bytes */ - val = frame[0x0]; - val |= frame[0x1] << 8; - val |= frame[0x2] << 16; - val |= frame[0x3] << 24; + val = buffer[3]; + val |= *(frame++) << 8; + val |= *(frame++) << 16; + val |= *(frame++) << 24; hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI)); - val = frame[0x4]; - val |= frame[0x5] << 8; - val |= frame[0x6] << 16; - val |= frame[0x7] << 24; + val = *(frame++); + val |= *(frame++) << 8; + val |= *(frame++) << 16; + val |= *(frame++) << 24; hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI)); - val = frame[0x8]; - val |= frame[0x9] << 8; - val |= frame[0xA] << 16; - val |= frame[0xB] << 24; + val = *(frame++); + val |= *(frame++) << 8; + val |= *(frame++) << 16; + val |= *(frame++) << 24; hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI)); - val = frame[0xC]; + val = *(frame++); + val |= *(frame) << 8; hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI)); /* Enable transmission slot for AVI infoframe @@ -480,17 +480,15 @@ static const struct drm_bridge_funcs sti_hdmi_bridge_funcs = { static int sti_hdmi_connector_get_modes(struct drm_connector *connector) { - struct i2c_adapter *i2c_adap; + struct sti_hdmi_connector *hdmi_connector + = to_sti_hdmi_connector(connector); + struct sti_hdmi *hdmi = hdmi_connector->hdmi; struct edid *edid; int count; DRM_DEBUG_DRIVER("\n"); - i2c_adap = i2c_get_adapter(1); - if (!i2c_adap) - goto fail; - - edid = drm_get_edid(connector, i2c_adap); + edid = drm_get_edid(connector, hdmi->ddc_adapt); if (!edid) goto fail; @@ -603,29 +601,38 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) struct sti_hdmi_connector *connector; struct drm_connector *drm_connector; struct drm_bridge *bridge; - struct i2c_adapter *i2c_adap; + struct device_node *ddc; int err; - i2c_adap = i2c_get_adapter(1); - if (!i2c_adap) - return -EPROBE_DEFER; + ddc = of_parse_phandle(dev->of_node, "ddc", 0); + if (ddc) { + hdmi->ddc_adapt = of_find_i2c_adapter_by_node(ddc); + if (!hdmi->ddc_adapt) { + err = -EPROBE_DEFER; + of_node_put(ddc); + return err; + } + + of_node_put(ddc); + } /* Set the drm device handle */ hdmi->drm_dev = drm_dev; encoder = sti_hdmi_find_encoder(drm_dev); if (!encoder) - return -ENOMEM; + goto err_adapt; connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL); if (!connector) - return -ENOMEM; + goto err_adapt; + connector->hdmi = hdmi; bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); if (!bridge) - return -ENOMEM; + goto err_adapt; bridge->driver_private = hdmi; drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs); @@ -662,6 +669,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) err_connector: drm_bridge_cleanup(bridge); drm_connector_cleanup(drm_connector); +err_adapt: + put_device(&hdmi->ddc_adapt->dev); return -EINVAL; } @@ -757,13 +766,7 @@ static int sti_hdmi_probe(struct platform_device *pdev) return PTR_ERR(hdmi->clk_audio); } - hdmi->hpd_gpio = of_get_named_gpio(np, "hdmi,hpd-gpio", 0); - if (hdmi->hpd_gpio < 0) { - DRM_ERROR("Failed to get hdmi hpd-gpio\n"); - return -EIO; - } - - hdmi->hpd = gpio_get_value(hdmi->hpd_gpio); + hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG; init_waitqueue_head(&hdmi->wait_event); @@ -788,6 +791,11 @@ static int sti_hdmi_probe(struct platform_device *pdev) static int sti_hdmi_remove(struct platform_device *pdev) { + struct sti_hdmi *hdmi = dev_get_drvdata(&pdev->dev); + + if (hdmi->ddc_adapt) + put_device(&hdmi->ddc_adapt->dev); + component_del(&pdev->dev, &sti_hdmi_ops); return 0; } diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h index 61bec6557cebc7709654e816b02d2901f72edec8..3d22390e1f3b3a3b5ad122398853a8a999b66fa1 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.h +++ b/drivers/gpu/drm/sti/sti_hdmi.h @@ -14,6 +14,9 @@ #define HDMI_STA 0x0010 #define HDMI_STA_DLL_LCK BIT(5) +#define HDMI_STA_HOT_PLUG_SHIFT 4 +#define HDMI_STA_HOT_PLUG (1 << HDMI_STA_HOT_PLUG_SHIFT) + struct sti_hdmi; struct hdmi_phy_ops { @@ -37,7 +40,6 @@ struct hdmi_phy_ops { * @irq_status: interrupt status register * @phy_ops: phy start/stop operations * @enabled: true if hdmi is enabled else false - * @hpd_gpio: hdmi hot plug detect gpio number * @hpd: hot plug detect status * @wait_event: wait event * @event_received: wait event status @@ -57,11 +59,11 @@ struct sti_hdmi { u32 irq_status; struct hdmi_phy_ops *phy_ops; bool enabled; - int hpd_gpio; bool hpd; wait_queue_head_t wait_event; bool event_received; struct reset_control *reset; + struct i2c_adapter *ddc_adapt; }; u32 hdmi_read(struct sti_hdmi *hdmi, int offset); diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c new file mode 100644 index 0000000000000000000000000000000000000000..200d0201457521e63e2072a6e0bc39b16e92ac6d --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hqvdp.c @@ -0,0 +1,1072 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "sti_drm_plane.h" +#include "sti_hqvdp.h" +#include "sti_hqvdp_lut.h" +#include "sti_layer.h" +#include "sti_vtg.h" + +/* Firmware name */ +#define HQVDP_FMW_NAME "hqvdp-stih407.bin" + +/* Regs address */ +#define HQVDP_DMEM 0x00000000 /* 0x00000000 */ +#define HQVDP_PMEM 0x00040000 /* 0x00040000 */ +#define HQVDP_RD_PLUG 0x000E0000 /* 0x000E0000 */ +#define HQVDP_RD_PLUG_CONTROL (HQVDP_RD_PLUG + 0x1000) /* 0x000E1000 */ +#define HQVDP_RD_PLUG_PAGE_SIZE (HQVDP_RD_PLUG + 0x1004) /* 0x000E1004 */ +#define HQVDP_RD_PLUG_MIN_OPC (HQVDP_RD_PLUG + 0x1008) /* 0x000E1008 */ +#define HQVDP_RD_PLUG_MAX_OPC (HQVDP_RD_PLUG + 0x100C) /* 0x000E100C */ +#define HQVDP_RD_PLUG_MAX_CHK (HQVDP_RD_PLUG + 0x1010) /* 0x000E1010 */ +#define HQVDP_RD_PLUG_MAX_MSG (HQVDP_RD_PLUG + 0x1014) /* 0x000E1014 */ +#define HQVDP_RD_PLUG_MIN_SPACE (HQVDP_RD_PLUG + 0x1018) /* 0x000E1018 */ +#define HQVDP_WR_PLUG 0x000E2000 /* 0x000E2000 */ +#define HQVDP_WR_PLUG_CONTROL (HQVDP_WR_PLUG + 0x1000) /* 0x000E3000 */ +#define HQVDP_WR_PLUG_PAGE_SIZE (HQVDP_WR_PLUG + 0x1004) /* 0x000E3004 */ +#define HQVDP_WR_PLUG_MIN_OPC (HQVDP_WR_PLUG + 0x1008) /* 0x000E3008 */ +#define HQVDP_WR_PLUG_MAX_OPC (HQVDP_WR_PLUG + 0x100C) /* 0x000E300C */ +#define HQVDP_WR_PLUG_MAX_CHK (HQVDP_WR_PLUG + 0x1010) /* 0x000E3010 */ +#define HQVDP_WR_PLUG_MAX_MSG (HQVDP_WR_PLUG + 0x1014) /* 0x000E3014 */ +#define HQVDP_WR_PLUG_MIN_SPACE (HQVDP_WR_PLUG + 0x1018) /* 0x000E3018 */ +#define HQVDP_MBX 0x000E4000 /* 0x000E4000 */ +#define HQVDP_MBX_IRQ_TO_XP70 (HQVDP_MBX + 0x0000) /* 0x000E4000 */ +#define HQVDP_MBX_INFO_HOST (HQVDP_MBX + 0x0004) /* 0x000E4004 */ +#define HQVDP_MBX_IRQ_TO_HOST (HQVDP_MBX + 0x0008) /* 0x000E4008 */ +#define HQVDP_MBX_INFO_XP70 (HQVDP_MBX + 0x000C) /* 0x000E400C */ +#define HQVDP_MBX_SW_RESET_CTRL (HQVDP_MBX + 0x0010) /* 0x000E4010 */ +#define HQVDP_MBX_STARTUP_CTRL1 (HQVDP_MBX + 0x0014) /* 0x000E4014 */ +#define HQVDP_MBX_STARTUP_CTRL2 (HQVDP_MBX + 0x0018) /* 0x000E4018 */ +#define HQVDP_MBX_GP_STATUS (HQVDP_MBX + 0x001C) /* 0x000E401C */ +#define HQVDP_MBX_NEXT_CMD (HQVDP_MBX + 0x0020) /* 0x000E4020 */ +#define HQVDP_MBX_CURRENT_CMD (HQVDP_MBX + 0x0024) /* 0x000E4024 */ +#define HQVDP_MBX_SOFT_VSYNC (HQVDP_MBX + 0x0028) /* 0x000E4028 */ + +/* Plugs config */ +#define PLUG_CONTROL_ENABLE 0x00000001 +#define PLUG_PAGE_SIZE_256 0x00000002 +#define PLUG_MIN_OPC_8 0x00000003 +#define PLUG_MAX_OPC_64 0x00000006 +#define PLUG_MAX_CHK_2X 0x00000001 +#define PLUG_MAX_MSG_1X 0x00000000 +#define PLUG_MIN_SPACE_1 0x00000000 + +/* SW reset CTRL */ +#define SW_RESET_CTRL_FULL BIT(0) +#define SW_RESET_CTRL_CORE BIT(1) + +/* Startup ctrl 1 */ +#define STARTUP_CTRL1_RST_DONE BIT(0) +#define STARTUP_CTRL1_AUTH_IDLE BIT(2) + +/* Startup ctrl 2 */ +#define STARTUP_CTRL2_FETCH_EN BIT(1) + +/* Info xP70 */ +#define INFO_XP70_FW_READY BIT(15) +#define INFO_XP70_FW_PROCESSING BIT(14) +#define INFO_XP70_FW_INITQUEUES BIT(13) + +/* SOFT_VSYNC */ +#define SOFT_VSYNC_HW 0x00000000 +#define SOFT_VSYNC_SW_CMD 0x00000001 +#define SOFT_VSYNC_SW_CTRL_IRQ 0x00000003 + +/* Reset & boot poll config */ +#define POLL_MAX_ATTEMPT 50 +#define POLL_DELAY_MS 20 + +#define SCALE_FACTOR 8192 +#define SCALE_MAX_FOR_LEG_LUT_F 4096 +#define SCALE_MAX_FOR_LEG_LUT_E 4915 +#define SCALE_MAX_FOR_LEG_LUT_D 6654 +#define SCALE_MAX_FOR_LEG_LUT_C 8192 + +enum sti_hvsrc_orient { + HVSRC_HORI, + HVSRC_VERT +}; + +/* Command structures */ +struct sti_hqvdp_top { + u32 config; + u32 mem_format; + u32 current_luma; + u32 current_enh_luma; + u32 current_right_luma; + u32 current_enh_right_luma; + u32 current_chroma; + u32 current_enh_chroma; + u32 current_right_chroma; + u32 current_enh_right_chroma; + u32 output_luma; + u32 output_chroma; + u32 luma_src_pitch; + u32 luma_enh_src_pitch; + u32 luma_right_src_pitch; + u32 luma_enh_right_src_pitch; + u32 chroma_src_pitch; + u32 chroma_enh_src_pitch; + u32 chroma_right_src_pitch; + u32 chroma_enh_right_src_pitch; + u32 luma_processed_pitch; + u32 chroma_processed_pitch; + u32 input_frame_size; + u32 input_viewport_ori; + u32 input_viewport_ori_right; + u32 input_viewport_size; + u32 left_view_border_width; + u32 right_view_border_width; + u32 left_view_3d_offset_width; + u32 right_view_3d_offset_width; + u32 side_stripe_color; + u32 crc_reset_ctrl; +}; + +/* Configs for interlaced : no IT, no pass thru, 3 fields */ +#define TOP_CONFIG_INTER_BTM 0x00000000 +#define TOP_CONFIG_INTER_TOP 0x00000002 + +/* Config for progressive : no IT, no pass thru, 3 fields */ +#define TOP_CONFIG_PROGRESSIVE 0x00000001 + +/* Default MemFormat: in=420_raster_dual out=444_raster;opaque Mem2Tv mode */ +#define TOP_MEM_FORMAT_DFLT 0x00018060 + +/* Min/Max size */ +#define MAX_WIDTH 0x1FFF +#define MAX_HEIGHT 0x0FFF +#define MIN_WIDTH 0x0030 +#define MIN_HEIGHT 0x0010 + +struct sti_hqvdp_vc1re { + u32 ctrl_prv_csdi; + u32 ctrl_cur_csdi; + u32 ctrl_nxt_csdi; + u32 ctrl_cur_fmd; + u32 ctrl_nxt_fmd; +}; + +struct sti_hqvdp_fmd { + u32 config; + u32 viewport_ori; + u32 viewport_size; + u32 next_next_luma; + u32 next_next_right_luma; + u32 next_next_next_luma; + u32 next_next_next_right_luma; + u32 threshold_scd; + u32 threshold_rfd; + u32 threshold_move; + u32 threshold_cfd; +}; + +struct sti_hqvdp_csdi { + u32 config; + u32 config2; + u32 dcdi_config; + u32 prev_luma; + u32 prev_enh_luma; + u32 prev_right_luma; + u32 prev_enh_right_luma; + u32 next_luma; + u32 next_enh_luma; + u32 next_right_luma; + u32 next_enh_right_luma; + u32 prev_chroma; + u32 prev_enh_chroma; + u32 prev_right_chroma; + u32 prev_enh_right_chroma; + u32 next_chroma; + u32 next_enh_chroma; + u32 next_right_chroma; + u32 next_enh_right_chroma; + u32 prev_motion; + u32 prev_right_motion; + u32 cur_motion; + u32 cur_right_motion; + u32 next_motion; + u32 next_right_motion; +}; + +/* Config for progressive: by pass */ +#define CSDI_CONFIG_PROG 0x00000000 +/* Config for directional deinterlacing without motion */ +#define CSDI_CONFIG_INTER_DIR 0x00000016 +/* Additional configs for fader, blender, motion,... deinterlace algorithms */ +#define CSDI_CONFIG2_DFLT 0x000001B3 +#define CSDI_DCDI_CONFIG_DFLT 0x00203803 + +struct sti_hqvdp_hvsrc { + u32 hor_panoramic_ctrl; + u32 output_picture_size; + u32 init_horizontal; + u32 init_vertical; + u32 param_ctrl; + u32 yh_coef[NB_COEF]; + u32 ch_coef[NB_COEF]; + u32 yv_coef[NB_COEF]; + u32 cv_coef[NB_COEF]; + u32 hori_shift; + u32 vert_shift; +}; + +/* Default ParamCtrl: all controls enabled */ +#define HVSRC_PARAM_CTRL_DFLT 0xFFFFFFFF + +struct sti_hqvdp_iqi { + u32 config; + u32 demo_wind_size; + u32 pk_config; + u32 coeff0_coeff1; + u32 coeff2_coeff3; + u32 coeff4; + u32 pk_lut; + u32 pk_gain; + u32 pk_coring_level; + u32 cti_config; + u32 le_config; + u32 le_lut[64]; + u32 con_bri; + u32 sat_gain; + u32 pxf_conf; + u32 default_color; +}; + +/* Default Config : IQI bypassed */ +#define IQI_CONFIG_DFLT 0x00000001 +/* Default Contrast & Brightness gain = 256 */ +#define IQI_CON_BRI_DFLT 0x00000100 +/* Default Saturation gain = 256 */ +#define IQI_SAT_GAIN_DFLT 0x00000100 +/* Default PxfConf : P2I bypassed */ +#define IQI_PXF_CONF_DFLT 0x00000001 + +struct sti_hqvdp_top_status { + u32 processing_time; + u32 input_y_crc; + u32 input_uv_crc; +}; + +struct sti_hqvdp_fmd_status { + u32 fmd_repeat_move_status; + u32 fmd_scene_count_status; + u32 cfd_sum; + u32 field_sum; + u32 next_y_fmd_crc; + u32 next_next_y_fmd_crc; + u32 next_next_next_y_fmd_crc; +}; + +struct sti_hqvdp_csdi_status { + u32 prev_y_csdi_crc; + u32 cur_y_csdi_crc; + u32 next_y_csdi_crc; + u32 prev_uv_csdi_crc; + u32 cur_uv_csdi_crc; + u32 next_uv_csdi_crc; + u32 y_csdi_crc; + u32 uv_csdi_crc; + u32 uv_cup_crc; + u32 mot_csdi_crc; + u32 mot_cur_csdi_crc; + u32 mot_prev_csdi_crc; +}; + +struct sti_hqvdp_hvsrc_status { + u32 y_hvsrc_crc; + u32 u_hvsrc_crc; + u32 v_hvsrc_crc; +}; + +struct sti_hqvdp_iqi_status { + u32 pxf_it_status; + u32 y_iqi_crc; + u32 u_iqi_crc; + u32 v_iqi_crc; +}; + +/* Main commands. We use 2 commands one being processed by the firmware, one + * ready to be fetched upon next Vsync*/ +#define NB_VDP_CMD 2 + +struct sti_hqvdp_cmd { + struct sti_hqvdp_top top; + struct sti_hqvdp_vc1re vc1re; + struct sti_hqvdp_fmd fmd; + struct sti_hqvdp_csdi csdi; + struct sti_hqvdp_hvsrc hvsrc; + struct sti_hqvdp_iqi iqi; + struct sti_hqvdp_top_status top_status; + struct sti_hqvdp_fmd_status fmd_status; + struct sti_hqvdp_csdi_status csdi_status; + struct sti_hqvdp_hvsrc_status hvsrc_status; + struct sti_hqvdp_iqi_status iqi_status; +}; + +/* + * STI HQVDP structure + * + * @dev: driver device + * @drm_dev: the drm device + * @regs: registers + * @layer: layer structure for hqvdp it self + * @vid_plane: VID plug used as link with compositor IP + * @clk: IP clock + * @clk_pix_main: pix main clock + * @reset: reset control + * @vtg_nb: notifier to handle VTG Vsync + * @btm_field_pending: is there any bottom field (interlaced frame) to display + * @curr_field_count: number of field updates + * @last_field_count: number of field updates since last fps measure + * @hqvdp_cmd: buffer of commands + * @hqvdp_cmd_paddr: physical address of hqvdp_cmd + * @vtg: vtg for main data path + */ +struct sti_hqvdp { + struct device *dev; + struct drm_device *drm_dev; + void __iomem *regs; + struct sti_layer layer; + struct drm_plane *vid_plane; + struct clk *clk; + struct clk *clk_pix_main; + struct reset_control *reset; + struct notifier_block vtg_nb; + bool btm_field_pending; + unsigned int curr_field_count; + unsigned int last_field_count; + void *hqvdp_cmd; + dma_addr_t hqvdp_cmd_paddr; + struct sti_vtg *vtg; +}; + +#define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, layer) + +static const uint32_t hqvdp_supported_formats[] = { + DRM_FORMAT_NV12, +}; + +static const uint32_t *sti_hqvdp_get_formats(struct sti_layer *layer) +{ + return hqvdp_supported_formats; +} + +static unsigned int sti_hqvdp_get_nb_formats(struct sti_layer *layer) +{ + return ARRAY_SIZE(hqvdp_supported_formats); +} + +/** + * sti_hqvdp_get_free_cmd + * @hqvdp: hqvdp structure + * + * Look for a hqvdp_cmd that is not being used (or about to be used) by the FW. + * + * RETURNS: + * the offset of the command to be used. + * -1 in error cases + */ +static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp) +{ + int curr_cmd, next_cmd; + dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; + int i; + + curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); + next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD); + + for (i = 0; i < NB_VDP_CMD; i++) { + if ((cmd != curr_cmd) && (cmd != next_cmd)) + return i * sizeof(struct sti_hqvdp_cmd); + cmd += sizeof(struct sti_hqvdp_cmd); + } + + return -1; +} + +/** + * sti_hqvdp_get_curr_cmd + * @hqvdp: hqvdp structure + * + * Look for the hqvdp_cmd that is being used by the FW. + * + * RETURNS: + * the offset of the command to be used. + * -1 in error cases + */ +static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp) +{ + int curr_cmd; + dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr; + unsigned int i; + + curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD); + + for (i = 0; i < NB_VDP_CMD; i++) { + if (cmd == curr_cmd) + return i * sizeof(struct sti_hqvdp_cmd); + + cmd += sizeof(struct sti_hqvdp_cmd); + } + + return -1; +} + +/** + * sti_hqvdp_update_hvsrc + * @orient: horizontal or vertical + * @scale: scaling/zoom factor + * @hvsrc: the structure containing the LUT coef + * + * Update the Y and C Lut coef, as well as the shift param + * + * RETURNS: + * None. + */ +static void sti_hqvdp_update_hvsrc(enum sti_hvsrc_orient orient, int scale, + struct sti_hqvdp_hvsrc *hvsrc) +{ + const int *coef_c, *coef_y; + int shift_c, shift_y; + + /* Get the appropriate coef tables */ + if (scale < SCALE_MAX_FOR_LEG_LUT_F) { + coef_y = coef_lut_f_y_legacy; + coef_c = coef_lut_f_c_legacy; + shift_y = SHIFT_LUT_F_Y_LEGACY; + shift_c = SHIFT_LUT_F_C_LEGACY; + } else if (scale < SCALE_MAX_FOR_LEG_LUT_E) { + coef_y = coef_lut_e_y_legacy; + coef_c = coef_lut_e_c_legacy; + shift_y = SHIFT_LUT_E_Y_LEGACY; + shift_c = SHIFT_LUT_E_C_LEGACY; + } else if (scale < SCALE_MAX_FOR_LEG_LUT_D) { + coef_y = coef_lut_d_y_legacy; + coef_c = coef_lut_d_c_legacy; + shift_y = SHIFT_LUT_D_Y_LEGACY; + shift_c = SHIFT_LUT_D_C_LEGACY; + } else if (scale < SCALE_MAX_FOR_LEG_LUT_C) { + coef_y = coef_lut_c_y_legacy; + coef_c = coef_lut_c_c_legacy; + shift_y = SHIFT_LUT_C_Y_LEGACY; + shift_c = SHIFT_LUT_C_C_LEGACY; + } else if (scale == SCALE_MAX_FOR_LEG_LUT_C) { + coef_y = coef_c = coef_lut_b; + shift_y = shift_c = SHIFT_LUT_B; + } else { + coef_y = coef_c = coef_lut_a_legacy; + shift_y = shift_c = SHIFT_LUT_A_LEGACY; + } + + if (orient == HVSRC_HORI) { + hvsrc->hori_shift = (shift_c << 16) | shift_y; + memcpy(hvsrc->yh_coef, coef_y, sizeof(hvsrc->yh_coef)); + memcpy(hvsrc->ch_coef, coef_c, sizeof(hvsrc->ch_coef)); + } else { + hvsrc->vert_shift = (shift_c << 16) | shift_y; + memcpy(hvsrc->yv_coef, coef_y, sizeof(hvsrc->yv_coef)); + memcpy(hvsrc->cv_coef, coef_c, sizeof(hvsrc->cv_coef)); + } +} + +/** + * sti_hqvdp_check_hw_scaling + * @layer: hqvdp layer + * + * Check if the HW is able to perform the scaling request + * The firmware scaling limitation is "CEIL(1/Zy) <= FLOOR(LFW)" where: + * Zy = OutputHeight / InputHeight + * LFW = (Tx * IPClock) / (MaxNbCycles * Cp) + * Tx : Total video mode horizontal resolution + * IPClock : HQVDP IP clock (Mhz) + * MaxNbCycles: max(InputWidth, OutputWidth) + * Cp: Video mode pixel clock (Mhz) + * + * RETURNS: + * True if the HW can scale. + */ +static bool sti_hqvdp_check_hw_scaling(struct sti_layer *layer) +{ + struct sti_hqvdp *hqvdp = to_sti_hqvdp(layer); + unsigned long lfw; + unsigned int inv_zy; + + lfw = layer->mode->htotal * (clk_get_rate(hqvdp->clk) / 1000000); + lfw /= max(layer->src_w, layer->dst_w) * layer->mode->clock / 1000; + + inv_zy = DIV_ROUND_UP(layer->src_h, layer->dst_h); + + return (inv_zy <= lfw) ? true : false; +} + +/** + * sti_hqvdp_prepare_layer + * @layer: hqvdp layer + * @first_prepare: true if it is the first time this function is called + * + * Prepares a command for the firmware + * + * RETURNS: + * 0 on success. + */ +static int sti_hqvdp_prepare_layer(struct sti_layer *layer, bool first_prepare) +{ + struct sti_hqvdp *hqvdp = to_sti_hqvdp(layer); + struct sti_hqvdp_cmd *cmd; + int scale_h, scale_v; + int cmd_offset; + + dev_dbg(hqvdp->dev, "%s %s\n", __func__, sti_layer_to_str(layer)); + + /* prepare and commit VID plane */ + hqvdp->vid_plane->funcs->update_plane(hqvdp->vid_plane, + layer->crtc, layer->fb, + layer->dst_x, layer->dst_y, + layer->dst_w, layer->dst_h, + layer->src_x, layer->src_y, + layer->src_w, layer->src_h); + + cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); + if (cmd_offset == -1) { + DRM_ERROR("No available hqvdp_cmd now\n"); + return -EBUSY; + } + cmd = hqvdp->hqvdp_cmd + cmd_offset; + + if (!sti_hqvdp_check_hw_scaling(layer)) { + DRM_ERROR("Scaling beyond HW capabilities\n"); + return -EINVAL; + } + + /* Static parameters, defaulting to progressive mode */ + cmd->top.config = TOP_CONFIG_PROGRESSIVE; + cmd->top.mem_format = TOP_MEM_FORMAT_DFLT; + cmd->hvsrc.param_ctrl = HVSRC_PARAM_CTRL_DFLT; + cmd->csdi.config = CSDI_CONFIG_PROG; + + /* VC1RE, FMD bypassed : keep everything set to 0 + * IQI/P2I bypassed */ + cmd->iqi.config = IQI_CONFIG_DFLT; + cmd->iqi.con_bri = IQI_CON_BRI_DFLT; + cmd->iqi.sat_gain = IQI_SAT_GAIN_DFLT; + cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT; + + /* Buffer planes address */ + cmd->top.current_luma = (u32) layer->paddr + layer->offsets[0]; + cmd->top.current_chroma = (u32) layer->paddr + layer->offsets[1]; + + /* Pitches */ + cmd->top.luma_processed_pitch = cmd->top.luma_src_pitch = + layer->pitches[0]; + cmd->top.chroma_processed_pitch = cmd->top.chroma_src_pitch = + layer->pitches[1]; + + /* Input / output size + * Align to upper even value */ + layer->dst_w = ALIGN(layer->dst_w, 2); + layer->dst_h = ALIGN(layer->dst_h, 2); + + if ((layer->src_w > MAX_WIDTH) || (layer->src_w < MIN_WIDTH) || + (layer->src_h > MAX_HEIGHT) || (layer->src_h < MIN_HEIGHT) || + (layer->dst_w > MAX_WIDTH) || (layer->dst_w < MIN_WIDTH) || + (layer->dst_h > MAX_HEIGHT) || (layer->dst_h < MIN_HEIGHT)) { + DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n", + layer->src_w, layer->src_h, + layer->dst_w, layer->dst_h); + return -EINVAL; + } + cmd->top.input_viewport_size = cmd->top.input_frame_size = + layer->src_h << 16 | layer->src_w; + cmd->hvsrc.output_picture_size = layer->dst_h << 16 | layer->dst_w; + cmd->top.input_viewport_ori = layer->src_y << 16 | layer->src_x; + + /* Handle interlaced */ + if (layer->fb->flags & DRM_MODE_FB_INTERLACED) { + /* Top field to display */ + cmd->top.config = TOP_CONFIG_INTER_TOP; + + /* Update pitches and vert size */ + cmd->top.input_frame_size = (layer->src_h / 2) << 16 | + layer->src_w; + cmd->top.luma_processed_pitch *= 2; + cmd->top.luma_src_pitch *= 2; + cmd->top.chroma_processed_pitch *= 2; + cmd->top.chroma_src_pitch *= 2; + + /* Enable directional deinterlacing processing */ + cmd->csdi.config = CSDI_CONFIG_INTER_DIR; + cmd->csdi.config2 = CSDI_CONFIG2_DFLT; + cmd->csdi.dcdi_config = CSDI_DCDI_CONFIG_DFLT; + } + + /* Update hvsrc lut coef */ + scale_h = SCALE_FACTOR * layer->dst_w / layer->src_w; + sti_hqvdp_update_hvsrc(HVSRC_HORI, scale_h, &cmd->hvsrc); + + scale_v = SCALE_FACTOR * layer->dst_h / layer->src_h; + sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc); + + if (first_prepare) { + /* Prevent VTG shutdown */ + if (clk_prepare_enable(hqvdp->clk_pix_main)) { + DRM_ERROR("Failed to prepare/enable pix main clk\n"); + return -ENXIO; + } + + /* Register VTG Vsync callback to handle bottom fields */ + if ((layer->fb->flags & DRM_MODE_FB_INTERLACED) && + sti_vtg_register_client(hqvdp->vtg, + &hqvdp->vtg_nb, layer->mixer_id)) { + DRM_ERROR("Cannot register VTG notifier\n"); + return -ENXIO; + } + } + + return 0; +} + +static int sti_hqvdp_commit_layer(struct sti_layer *layer) +{ + struct sti_hqvdp *hqvdp = to_sti_hqvdp(layer); + int cmd_offset; + + dev_dbg(hqvdp->dev, "%s %s\n", __func__, sti_layer_to_str(layer)); + + cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); + if (cmd_offset == -1) { + DRM_ERROR("No available hqvdp_cmd now\n"); + return -EBUSY; + } + + writel(hqvdp->hqvdp_cmd_paddr + cmd_offset, + hqvdp->regs + HQVDP_MBX_NEXT_CMD); + + hqvdp->curr_field_count++; + + /* Interlaced : get ready to display the bottom field at next Vsync */ + if (layer->fb->flags & DRM_MODE_FB_INTERLACED) + hqvdp->btm_field_pending = true; + + dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", + __func__, hqvdp->hqvdp_cmd_paddr + cmd_offset); + + return 0; +} + +static int sti_hqvdp_disable_layer(struct sti_layer *layer) +{ + struct sti_hqvdp *hqvdp = to_sti_hqvdp(layer); + int i; + + DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer)); + + /* Unregister VTG Vsync callback */ + if ((layer->fb->flags & DRM_MODE_FB_INTERLACED) && + sti_vtg_unregister_client(hqvdp->vtg, &hqvdp->vtg_nb)) + DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); + + /* Set next cmd to NULL */ + writel(0, hqvdp->regs + HQVDP_MBX_NEXT_CMD); + + for (i = 0; i < POLL_MAX_ATTEMPT; i++) { + if (readl(hqvdp->regs + HQVDP_MBX_INFO_XP70) + & INFO_XP70_FW_READY) + break; + msleep(POLL_DELAY_MS); + } + + /* VTG can stop now */ + clk_disable_unprepare(hqvdp->clk_pix_main); + + if (i == POLL_MAX_ATTEMPT) { + DRM_ERROR("XP70 could not revert to idle\n"); + return -ENXIO; + } + + /* disable VID plane */ + hqvdp->vid_plane->funcs->disable_plane(hqvdp->vid_plane); + + return 0; +} + +/** + * sti_vdp_vtg_cb + * @nb: notifier block + * @evt: event message + * @data: private data + * + * Handle VTG Vsync event, display pending bottom field + * + * RETURNS: + * 0 on success. + */ +int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data) +{ + struct sti_hqvdp *hqvdp = container_of(nb, struct sti_hqvdp, vtg_nb); + int btm_cmd_offset, top_cmd_offest; + struct sti_hqvdp_cmd *btm_cmd, *top_cmd; + + if ((evt != VTG_TOP_FIELD_EVENT) && (evt != VTG_BOTTOM_FIELD_EVENT)) { + DRM_DEBUG_DRIVER("Unknown event\n"); + return 0; + } + + if (hqvdp->btm_field_pending) { + /* Create the btm field command from the current one */ + btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp); + top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp); + if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) { + DRM_ERROR("Cannot get cmds, skip btm field\n"); + return -EBUSY; + } + + btm_cmd = hqvdp->hqvdp_cmd + btm_cmd_offset; + top_cmd = hqvdp->hqvdp_cmd + top_cmd_offest; + + memcpy(btm_cmd, top_cmd, sizeof(*btm_cmd)); + + btm_cmd->top.config = TOP_CONFIG_INTER_BTM; + btm_cmd->top.current_luma += + btm_cmd->top.luma_src_pitch / 2; + btm_cmd->top.current_chroma += + btm_cmd->top.chroma_src_pitch / 2; + + /* Post the command to mailbox */ + writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset, + hqvdp->regs + HQVDP_MBX_NEXT_CMD); + + hqvdp->curr_field_count++; + hqvdp->btm_field_pending = false; + + dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n", + __func__, hqvdp->hqvdp_cmd_paddr); + } + + return 0; +} + +static struct drm_plane *sti_hqvdp_find_vid(struct drm_device *dev, int id) +{ + struct drm_plane *plane; + + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + struct sti_layer *layer = to_sti_layer(plane); + + if (layer->desc == id) + return plane; + } + + return NULL; +} + +static void sti_hqvd_init(struct sti_layer *layer) +{ + struct sti_hqvdp *hqvdp = to_sti_hqvdp(layer); + int size; + + /* find the plane macthing with vid 0 */ + hqvdp->vid_plane = sti_hqvdp_find_vid(hqvdp->drm_dev, STI_VID_0); + if (!hqvdp->vid_plane) { + DRM_ERROR("Cannot find Main video layer\n"); + return; + } + + hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb; + + /* Allocate memory for the VDP commands */ + size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd); + hqvdp->hqvdp_cmd = dma_alloc_writecombine(hqvdp->dev, size, + &hqvdp->hqvdp_cmd_paddr, + GFP_KERNEL | GFP_DMA); + if (!hqvdp->hqvdp_cmd) { + DRM_ERROR("Failed to allocate memory for VDP cmd\n"); + return; + } + + memset(hqvdp->hqvdp_cmd, 0, size); +} + +static const struct sti_layer_funcs hqvdp_ops = { + .get_formats = sti_hqvdp_get_formats, + .get_nb_formats = sti_hqvdp_get_nb_formats, + .init = sti_hqvd_init, + .prepare = sti_hqvdp_prepare_layer, + .commit = sti_hqvdp_commit_layer, + .disable = sti_hqvdp_disable_layer, +}; + +struct sti_layer *sti_hqvdp_create(struct device *dev) +{ + struct sti_hqvdp *hqvdp = dev_get_drvdata(dev); + + hqvdp->layer.ops = &hqvdp_ops; + + return &hqvdp->layer; +} + +static void sti_hqvdp_init_plugs(struct sti_hqvdp *hqvdp) +{ + /* Configure Plugs (same for RD & WR) */ + writel(PLUG_PAGE_SIZE_256, hqvdp->regs + HQVDP_RD_PLUG_PAGE_SIZE); + writel(PLUG_MIN_OPC_8, hqvdp->regs + HQVDP_RD_PLUG_MIN_OPC); + writel(PLUG_MAX_OPC_64, hqvdp->regs + HQVDP_RD_PLUG_MAX_OPC); + writel(PLUG_MAX_CHK_2X, hqvdp->regs + HQVDP_RD_PLUG_MAX_CHK); + writel(PLUG_MAX_MSG_1X, hqvdp->regs + HQVDP_RD_PLUG_MAX_MSG); + writel(PLUG_MIN_SPACE_1, hqvdp->regs + HQVDP_RD_PLUG_MIN_SPACE); + writel(PLUG_CONTROL_ENABLE, hqvdp->regs + HQVDP_RD_PLUG_CONTROL); + + writel(PLUG_PAGE_SIZE_256, hqvdp->regs + HQVDP_WR_PLUG_PAGE_SIZE); + writel(PLUG_MIN_OPC_8, hqvdp->regs + HQVDP_WR_PLUG_MIN_OPC); + writel(PLUG_MAX_OPC_64, hqvdp->regs + HQVDP_WR_PLUG_MAX_OPC); + writel(PLUG_MAX_CHK_2X, hqvdp->regs + HQVDP_WR_PLUG_MAX_CHK); + writel(PLUG_MAX_MSG_1X, hqvdp->regs + HQVDP_WR_PLUG_MAX_MSG); + writel(PLUG_MIN_SPACE_1, hqvdp->regs + HQVDP_WR_PLUG_MIN_SPACE); + writel(PLUG_CONTROL_ENABLE, hqvdp->regs + HQVDP_WR_PLUG_CONTROL); +} + +/** + * sti_hqvdp_start_xp70 + * @firmware: firmware found + * @ctxt: hqvdp structure + * + * Run the xP70 initialization sequence + */ +static void sti_hqvdp_start_xp70(const struct firmware *firmware, void *ctxt) +{ + struct sti_hqvdp *hqvdp = ctxt; + u32 *fw_rd_plug, *fw_wr_plug, *fw_pmem, *fw_dmem; + u8 *data; + int i; + struct fw_header { + int rd_size; + int wr_size; + int pmem_size; + int dmem_size; + } *header; + + DRM_DEBUG_DRIVER("\n"); + /* Check firmware parts */ + if (!firmware) { + DRM_ERROR("Firmware not available\n"); + return; + } + + header = (struct fw_header *) firmware->data; + if (firmware->size < sizeof(*header)) { + DRM_ERROR("Invalid firmware size (%d)\n", firmware->size); + goto out; + } + if ((sizeof(*header) + header->rd_size + header->wr_size + + header->pmem_size + header->dmem_size) != firmware->size) { + DRM_ERROR("Invalid fmw structure (%d+%d+%d+%d+%d != %d)\n", + sizeof(*header), header->rd_size, header->wr_size, + header->pmem_size, header->dmem_size, + firmware->size); + goto out; + } + + data = (u8 *) firmware->data; + data += sizeof(*header); + fw_rd_plug = (void *) data; + data += header->rd_size; + fw_wr_plug = (void *) data; + data += header->wr_size; + fw_pmem = (void *) data; + data += header->pmem_size; + fw_dmem = (void *) data; + + /* Enable clock */ + if (clk_prepare_enable(hqvdp->clk)) + DRM_ERROR("Failed to prepare/enable HQVDP clk\n"); + + /* Reset */ + writel(SW_RESET_CTRL_FULL, hqvdp->regs + HQVDP_MBX_SW_RESET_CTRL); + + for (i = 0; i < POLL_MAX_ATTEMPT; i++) { + if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1) + & STARTUP_CTRL1_RST_DONE) + break; + msleep(POLL_DELAY_MS); + } + if (i == POLL_MAX_ATTEMPT) { + DRM_ERROR("Could not reset\n"); + goto out; + } + + /* Init Read & Write plugs */ + for (i = 0; i < header->rd_size / 4; i++) + writel(fw_rd_plug[i], hqvdp->regs + HQVDP_RD_PLUG + i * 4); + for (i = 0; i < header->wr_size / 4; i++) + writel(fw_wr_plug[i], hqvdp->regs + HQVDP_WR_PLUG + i * 4); + + sti_hqvdp_init_plugs(hqvdp); + + /* Authorize Idle Mode */ + writel(STARTUP_CTRL1_AUTH_IDLE, hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1); + + /* Prevent VTG interruption during the boot */ + writel(SOFT_VSYNC_SW_CTRL_IRQ, hqvdp->regs + HQVDP_MBX_SOFT_VSYNC); + writel(0, hqvdp->regs + HQVDP_MBX_NEXT_CMD); + + /* Download PMEM & DMEM */ + for (i = 0; i < header->pmem_size / 4; i++) + writel(fw_pmem[i], hqvdp->regs + HQVDP_PMEM + i * 4); + for (i = 0; i < header->dmem_size / 4; i++) + writel(fw_dmem[i], hqvdp->regs + HQVDP_DMEM + i * 4); + + /* Enable fetch */ + writel(STARTUP_CTRL2_FETCH_EN, hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2); + + /* Wait end of boot */ + for (i = 0; i < POLL_MAX_ATTEMPT; i++) { + if (readl(hqvdp->regs + HQVDP_MBX_INFO_XP70) + & INFO_XP70_FW_READY) + break; + msleep(POLL_DELAY_MS); + } + if (i == POLL_MAX_ATTEMPT) { + DRM_ERROR("Could not boot\n"); + goto out; + } + + /* Launch Vsync */ + writel(SOFT_VSYNC_HW, hqvdp->regs + HQVDP_MBX_SOFT_VSYNC); + + DRM_INFO("HQVDP XP70 started\n"); +out: + release_firmware(firmware); +} + +int sti_hqvdp_bind(struct device *dev, struct device *master, void *data) +{ + struct sti_hqvdp *hqvdp = dev_get_drvdata(dev); + struct drm_device *drm_dev = data; + struct sti_layer *layer; + int err; + + DRM_DEBUG_DRIVER("\n"); + + hqvdp->drm_dev = drm_dev; + + /* Request for firmware */ + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + HQVDP_FMW_NAME, hqvdp->dev, + GFP_KERNEL, hqvdp, sti_hqvdp_start_xp70); + if (err) { + DRM_ERROR("Can't get HQVDP firmware\n"); + return err; + } + + layer = sti_layer_create(hqvdp->dev, STI_HQVDP_0, hqvdp->regs); + if (!layer) { + DRM_ERROR("Can't create HQVDP plane\n"); + return -ENOMEM; + } + + sti_drm_plane_init(drm_dev, layer, 1, DRM_PLANE_TYPE_OVERLAY); + + return 0; +} + +static void sti_hqvdp_unbind(struct device *dev, + struct device *master, void *data) +{ + /* do nothing */ +} + +static const struct component_ops sti_hqvdp_ops = { + .bind = sti_hqvdp_bind, + .unbind = sti_hqvdp_unbind, +}; + +static int sti_hqvdp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *vtg_np; + struct sti_hqvdp *hqvdp; + struct resource *res; + + DRM_DEBUG_DRIVER("\n"); + + hqvdp = devm_kzalloc(dev, sizeof(*hqvdp), GFP_KERNEL); + if (!hqvdp) { + DRM_ERROR("Failed to allocate HQVDP context\n"); + return -ENOMEM; + } + + hqvdp->dev = dev; + + /* Get Memory resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + DRM_ERROR("Get memory resource failed\n"); + return -ENXIO; + } + hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res)); + if (hqvdp->regs == NULL) { + DRM_ERROR("Register mapping failed\n"); + return -ENXIO; + } + + /* Get clock resources */ + hqvdp->clk = devm_clk_get(dev, "hqvdp"); + hqvdp->clk_pix_main = devm_clk_get(dev, "pix_main"); + if (IS_ERR(hqvdp->clk) || IS_ERR(hqvdp->clk)) { + DRM_ERROR("Cannot get clocks\n"); + return -ENXIO; + } + + /* Get reset resources */ + hqvdp->reset = devm_reset_control_get(dev, "hqvdp"); + if (!IS_ERR(hqvdp->reset)) + reset_control_deassert(hqvdp->reset); + + vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0); + if (vtg_np) + hqvdp->vtg = of_vtg_find(vtg_np); + + platform_set_drvdata(pdev, hqvdp); + + return component_add(&pdev->dev, &sti_hqvdp_ops); +} + +static int sti_hqvdp_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sti_hqvdp_ops); + return 0; +} + +static struct of_device_id hqvdp_of_match[] = { + { .compatible = "st,stih407-hqvdp", }, + { /* end node */ } +}; +MODULE_DEVICE_TABLE(of, hqvdp_of_match); + +struct platform_driver sti_hqvdp_driver = { + .driver = { + .name = "sti-hqvdp", + .owner = THIS_MODULE, + .of_match_table = hqvdp_of_match, + }, + .probe = sti_hqvdp_probe, + .remove = sti_hqvdp_remove, +}; + +module_platform_driver(sti_hqvdp_driver); + +MODULE_AUTHOR("Benjamin Gaignard "); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/sti/sti_hqvdp.h b/drivers/gpu/drm/sti/sti_hqvdp.h new file mode 100644 index 0000000000000000000000000000000000000000..cd5ecd0a6dea5aa0acbafb32caf0c0bc35490401 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hqvdp.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_HQVDP_H_ +#define _STI_HQVDP_H_ + +struct sti_layer *sti_hqvdp_create(struct device *dev); + +#endif diff --git a/drivers/gpu/drm/sti/sti_hqvdp_lut.h b/drivers/gpu/drm/sti/sti_hqvdp_lut.h new file mode 100644 index 0000000000000000000000000000000000000000..619af7f4384e95286f608db750a57f8c9c573401 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_hqvdp_lut.h @@ -0,0 +1,373 @@ +/* + * Copyright (C) STMicroelectronics SA 2014 + * Authors: Fabien Dessenne for STMicroelectronics. + * License terms: GNU General Public License (GPL), version 2 + */ + +#ifndef _STI_HQVDP_LUT_H_ +#define _STI_HQVDP_LUT_H_ + +#define NB_COEF 128 + +#define SHIFT_LUT_A_LEGACY 8 +#define SHIFT_LUT_B 8 +#define SHIFT_LUT_C_Y_LEGACY 8 +#define SHIFT_LUT_C_C_LEGACY 8 +#define SHIFT_LUT_D_Y_LEGACY 8 +#define SHIFT_LUT_D_C_LEGACY 8 +#define SHIFT_LUT_E_Y_LEGACY 8 +#define SHIFT_LUT_E_C_LEGACY 8 +#define SHIFT_LUT_F_Y_LEGACY 8 +#define SHIFT_LUT_F_C_LEGACY 8 + +static const u32 coef_lut_a_legacy[NB_COEF] = { + 0x0000ffff, 0x00010000, 0x000100ff, 0x00000000, + 0x00000000, 0x00050000, 0xfffc00ff, 0x00000000, + 0x00000000, 0x00090000, 0xfff900fe, 0x00000000, + 0x00000000, 0x0010ffff, 0xfff600fb, 0x00000000, + 0x00000000, 0x0017fffe, 0xfff400f7, 0x00000000, + 0x00000000, 0x001ffffd, 0xfff200f2, 0x00000000, + 0x00000000, 0x0027fffc, 0xfff100ec, 0x00000000, + 0x00000000, 0x0030fffb, 0xfff000e5, 0x00000000, + 0x00000000, 0x003afffa, 0xffee00de, 0x00000000, + 0x00000000, 0x0044fff9, 0xffed00d6, 0x00000000, + 0x00000000, 0x004efff8, 0xffed00cd, 0x00000000, + 0x00000000, 0x0059fff6, 0xffed00c4, 0x00000000, + 0x00000000, 0x0064fff5, 0xffed00ba, 0x00000000, + 0x00000000, 0x006ffff3, 0xffee00b0, 0x00000000, + 0x00000000, 0x007afff2, 0xffee00a6, 0x00000000, + 0x00000000, 0x0085fff1, 0xffef009b, 0x00000000, + 0x00000000, 0x0090fff0, 0xfff00090, 0x00000000, + 0x00000000, 0x009bffef, 0xfff10085, 0x00000000, + 0x00000000, 0x00a6ffee, 0xfff2007a, 0x00000000, + 0x00000000, 0x00b0ffee, 0xfff3006f, 0x00000000, + 0x00000000, 0x00baffed, 0xfff50064, 0x00000000, + 0x00000000, 0x00c4ffed, 0xfff60059, 0x00000000, + 0x00000000, 0x00cdffed, 0xfff8004e, 0x00000000, + 0x00000000, 0x00d6ffed, 0xfff90044, 0x00000000, + 0x00000000, 0x00deffee, 0xfffa003a, 0x00000000, + 0x00000000, 0x00e5fff0, 0xfffb0030, 0x00000000, + 0x00000000, 0x00ecfff1, 0xfffc0027, 0x00000000, + 0x00000000, 0x00f2fff2, 0xfffd001f, 0x00000000, + 0x00000000, 0x00f7fff4, 0xfffe0017, 0x00000000, + 0x00000000, 0x00fbfff6, 0xffff0010, 0x00000000, + 0x00000000, 0x00fefff9, 0x00000009, 0x00000000, + 0x00000000, 0x00fffffc, 0x00000005, 0x00000000 +}; + +static const u32 coef_lut_b[NB_COEF] = { + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000, + 0x00000000, 0x00000000, 0x00000100, 0x00000000 +}; + +static const u32 coef_lut_c_y_legacy[NB_COEF] = { + 0x00060004, 0x0038ffe1, 0x003800be, 0x0006ffe1, + 0x00050005, 0x0042ffe1, 0x003800b3, 0x0007ffe1, + 0x00040006, 0x0046ffe1, 0x003300b2, 0x0008ffe2, + 0x00030007, 0x004cffe1, 0x002e00b1, 0x0008ffe2, + 0x00020006, 0x0051ffe2, 0x002900b0, 0x0009ffe3, + 0x00010008, 0x0056ffe2, 0x002400ae, 0x0009ffe4, + 0xffff0008, 0x005cffe3, 0x001f00ad, 0x000affe4, + 0xfffe0008, 0x0062ffe4, 0x001a00ab, 0x000affe5, + 0xfffd000a, 0x0066ffe5, 0x001500a8, 0x000bffe6, + 0xfffc0009, 0x006bffe7, 0x001100a5, 0x000bffe8, + 0xfffa000a, 0x0070ffe8, 0x000d00a3, 0x000bffe9, + 0xfff9000b, 0x0076ffea, 0x0008009f, 0x000bffea, + 0xfff7000b, 0x007affec, 0x0005009b, 0x000cffec, + 0xfff6000b, 0x007effef, 0x00010098, 0x000cffed, + 0xfff4000b, 0x0084fff1, 0xfffd0095, 0x000cffee, + 0xfff3000b, 0x0088fff4, 0xfffa0090, 0x000cfff0, + 0xfff1000b, 0x008dfff7, 0xfff7008d, 0x000bfff1, + 0xfff0000c, 0x0090fffa, 0xfff40088, 0x000bfff3, + 0xffee000c, 0x0095fffd, 0xfff10084, 0x000bfff4, + 0xffed000c, 0x00980001, 0xffef007e, 0x000bfff6, + 0xffec000c, 0x009b0005, 0xffec007a, 0x000bfff7, + 0xffea000b, 0x009f0008, 0xffea0076, 0x000bfff9, + 0xffe9000b, 0x00a3000d, 0xffe80070, 0x000afffa, + 0xffe8000b, 0x00a50011, 0xffe7006b, 0x0009fffc, + 0xffe6000b, 0x00a80015, 0xffe50066, 0x000afffd, + 0xffe5000a, 0x00ab001a, 0xffe40062, 0x0008fffe, + 0xffe4000a, 0x00ad001f, 0xffe3005c, 0x0008ffff, + 0xffe40009, 0x00ae0024, 0xffe20056, 0x00080001, + 0xffe30009, 0x00b00029, 0xffe20051, 0x00060002, + 0xffe20008, 0x00b1002e, 0xffe1004c, 0x00070003, + 0xffe20008, 0x00b20033, 0xffe10046, 0x00060004, + 0xffe10007, 0x00b30038, 0xffe10042, 0x00050005 +}; + +static const u32 coef_lut_c_c_legacy[NB_COEF] = { + 0x0001fff3, 0x003afffb, 0x003a00a1, 0x0001fffb, + 0x0001fff5, 0x0041fffb, 0x0038009a, 0x0001fffb, + 0x0001fff5, 0x0046fffb, 0x00340099, 0x0001fffb, + 0x0001fff7, 0x0049fffb, 0x00300098, 0x0001fffb, + 0x0001fff9, 0x004cfffb, 0x002d0096, 0x0001fffb, + 0x0001fffa, 0x004ffffc, 0x00290095, 0x0001fffb, + 0x0001fff9, 0x0054fffd, 0x00250093, 0x0001fffc, + 0x0001fffa, 0x0058fffd, 0x00220092, 0x0000fffc, + 0x0001fffb, 0x005bfffe, 0x001f0090, 0x0000fffc, + 0x0001fffd, 0x005effff, 0x001c008c, 0x0000fffd, + 0x0001fffd, 0x00620000, 0x0019008a, 0x0000fffd, + 0x0001fffe, 0x00660001, 0x00160088, 0xfffffffd, + 0x0000fffe, 0x006a0003, 0x00130085, 0xfffffffe, + 0x0000fffe, 0x006e0004, 0x00100083, 0xfffffffe, + 0x0000fffe, 0x00710006, 0x000e007f, 0xffffffff, + 0x0000fffe, 0x00750008, 0x000c007c, 0xfffeffff, + 0xfffffffe, 0x0079000a, 0x000a0079, 0xfffeffff, + 0xfffffffe, 0x007c000c, 0x00080075, 0xfffe0000, + 0xffffffff, 0x007f000e, 0x00060071, 0xfffe0000, + 0xfffeffff, 0x00830010, 0x0004006e, 0xfffe0000, + 0xfffeffff, 0x00850013, 0x0003006a, 0xfffe0000, + 0xfffdffff, 0x00880016, 0x00010066, 0xfffe0001, + 0xfffd0000, 0x008a0019, 0x00000062, 0xfffd0001, + 0xfffd0000, 0x008c001c, 0xffff005e, 0xfffd0001, + 0xfffc0000, 0x0090001f, 0xfffe005b, 0xfffb0001, + 0xfffc0000, 0x00920022, 0xfffd0058, 0xfffa0001, + 0xfffc0001, 0x00930025, 0xfffd0054, 0xfff90001, + 0xfffb0001, 0x00950029, 0xfffc004f, 0xfffa0001, + 0xfffb0001, 0x0096002d, 0xfffb004c, 0xfff90001, + 0xfffb0001, 0x00980030, 0xfffb0049, 0xfff70001, + 0xfffb0001, 0x00990034, 0xfffb0046, 0xfff50001, + 0xfffb0001, 0x009a0038, 0xfffb0041, 0xfff50001 +}; + +static const u32 coef_lut_d_y_legacy[NB_COEF] = { + 0xfff80009, 0x0046ffec, 0x004600a3, 0xfff8ffec, + 0xfff70009, 0x004effed, 0x0044009d, 0xfff9ffeb, + 0xfff6000a, 0x0052ffee, 0x003f009d, 0xfffaffea, + 0xfff50009, 0x0057ffef, 0x003b009d, 0xfffbffe9, + 0xfff50008, 0x005bfff0, 0x0037009c, 0xfffcffe9, + 0xfff40008, 0x005ffff2, 0x0033009b, 0xfffcffe9, + 0xfff30007, 0x0064fff3, 0x002f009b, 0xfffdffe8, + 0xfff20007, 0x0068fff5, 0x002b0099, 0xfffeffe8, + 0xfff10008, 0x006bfff7, 0x00270097, 0xffffffe8, + 0xfff00007, 0x006ffff9, 0x00230097, 0xffffffe8, + 0xffef0006, 0x0073fffb, 0x00200095, 0x0000ffe8, + 0xffee0005, 0x0077fffe, 0x001c0093, 0x0000ffe9, + 0xffee0005, 0x007a0000, 0x00180091, 0x0001ffe9, + 0xffed0005, 0x007d0003, 0x0015008e, 0x0002ffe9, + 0xffec0005, 0x00800006, 0x0012008b, 0x0002ffea, + 0xffeb0004, 0x00840008, 0x000e008a, 0x0003ffea, + 0xffeb0003, 0x0087000b, 0x000b0087, 0x0003ffeb, + 0xffea0003, 0x008a000e, 0x00080084, 0x0004ffeb, + 0xffea0002, 0x008b0012, 0x00060080, 0x0005ffec, + 0xffe90002, 0x008e0015, 0x0003007d, 0x0005ffed, + 0xffe90001, 0x00910018, 0x0000007a, 0x0005ffee, + 0xffe90000, 0x0093001c, 0xfffe0077, 0x0005ffee, + 0xffe80000, 0x00950020, 0xfffb0073, 0x0006ffef, + 0xffe8ffff, 0x00970023, 0xfff9006f, 0x0007fff0, + 0xffe8ffff, 0x00970027, 0xfff7006b, 0x0008fff1, + 0xffe8fffe, 0x0099002b, 0xfff50068, 0x0007fff2, + 0xffe8fffd, 0x009b002f, 0xfff30064, 0x0007fff3, + 0xffe9fffc, 0x009b0033, 0xfff2005f, 0x0008fff4, + 0xffe9fffc, 0x009c0037, 0xfff0005b, 0x0008fff5, + 0xffe9fffb, 0x009d003b, 0xffef0057, 0x0009fff5, + 0xffeafffa, 0x009d003f, 0xffee0052, 0x000afff6, + 0xffebfff9, 0x009d0044, 0xffed004e, 0x0009fff7 +}; + +static const u32 coef_lut_d_c_legacy[NB_COEF] = { + 0xfffeffff, 0x003fffff, 0x003f0089, 0xfffeffff, + 0xfffe0000, 0x00460000, 0x0042007d, 0xfffffffe, + 0xfffe0000, 0x00490001, 0x003f007d, 0xfffffffd, + 0xfffd0001, 0x004b0002, 0x003c007d, 0x0000fffc, + 0xfffd0001, 0x004e0003, 0x0039007c, 0x0000fffc, + 0xfffc0001, 0x00510005, 0x0036007c, 0x0000fffb, + 0xfffc0001, 0x00540006, 0x0033007b, 0x0001fffa, + 0xfffc0003, 0x00550008, 0x00310078, 0x0001fffa, + 0xfffb0003, 0x00580009, 0x002e0078, 0x0001fffa, + 0xfffb0002, 0x005b000b, 0x002b0077, 0x0002fff9, + 0xfffa0003, 0x005e000d, 0x00280075, 0x0002fff9, + 0xfffa0002, 0x0060000f, 0x00260074, 0x0002fff9, + 0xfffa0004, 0x00610011, 0x00230072, 0x0002fff9, + 0xfffa0004, 0x00640013, 0x00200070, 0x0002fff9, + 0xfff90004, 0x00660015, 0x001e006e, 0x0003fff9, + 0xfff90004, 0x00680017, 0x001c006c, 0x0003fff9, + 0xfff90003, 0x006b0019, 0x0019006b, 0x0003fff9, + 0xfff90003, 0x006c001c, 0x00170068, 0x0004fff9, + 0xfff90003, 0x006e001e, 0x00150066, 0x0004fff9, + 0xfff90002, 0x00700020, 0x00130064, 0x0004fffa, + 0xfff90002, 0x00720023, 0x00110061, 0x0004fffa, + 0xfff90002, 0x00740026, 0x000f0060, 0x0002fffa, + 0xfff90002, 0x00750028, 0x000d005e, 0x0003fffa, + 0xfff90002, 0x0077002b, 0x000b005b, 0x0002fffb, + 0xfffa0001, 0x0078002e, 0x00090058, 0x0003fffb, + 0xfffa0001, 0x00780031, 0x00080055, 0x0003fffc, + 0xfffa0001, 0x007b0033, 0x00060054, 0x0001fffc, + 0xfffb0000, 0x007c0036, 0x00050051, 0x0001fffc, + 0xfffc0000, 0x007c0039, 0x0003004e, 0x0001fffd, + 0xfffc0000, 0x007d003c, 0x0002004b, 0x0001fffd, + 0xfffdffff, 0x007d003f, 0x00010049, 0x0000fffe, + 0xfffeffff, 0x007d0042, 0x00000046, 0x0000fffe +}; + +static const u32 coef_lut_e_y_legacy[NB_COEF] = { + 0xfff10001, 0x00490004, 0x00490083, 0xfff10004, + 0xfff10000, 0x00500006, 0x004b007b, 0xfff10002, + 0xfff10000, 0x00530007, 0x0048007b, 0xfff10001, + 0xfff10000, 0x00550009, 0x0046007a, 0xfff10000, + 0xfff1fffe, 0x0058000b, 0x0043007b, 0xfff2fffe, + 0xfff1ffff, 0x005a000d, 0x0040007a, 0xfff2fffd, + 0xfff1fffd, 0x005d000f, 0x003e007a, 0xfff2fffc, + 0xfff1fffd, 0x005f0011, 0x003b0079, 0xfff3fffb, + 0xfff1fffc, 0x00610013, 0x00390079, 0xfff3fffa, + 0xfff1fffb, 0x00640015, 0x00360079, 0xfff3fff9, + 0xfff1fffa, 0x00660017, 0x00340078, 0xfff4fff8, + 0xfff1fffb, 0x00680019, 0x00310077, 0xfff4fff7, + 0xfff2fff9, 0x006a001b, 0x002f0076, 0xfff5fff6, + 0xfff2fff9, 0x006c001e, 0x002c0075, 0xfff5fff5, + 0xfff2fff9, 0x006d0020, 0x002a0073, 0xfff6fff5, + 0xfff3fff7, 0x00700022, 0x00270073, 0xfff6fff4, + 0xfff3fff7, 0x00710025, 0x00250071, 0xfff7fff3, + 0xfff4fff6, 0x00730027, 0x00220070, 0xfff7fff3, + 0xfff5fff6, 0x0073002a, 0x0020006d, 0xfff9fff2, + 0xfff5fff5, 0x0075002c, 0x001e006c, 0xfff9fff2, + 0xfff6fff5, 0x0076002f, 0x001b006a, 0xfff9fff2, + 0xfff7fff4, 0x00770031, 0x00190068, 0xfffbfff1, + 0xfff8fff4, 0x00780034, 0x00170066, 0xfffafff1, + 0xfff9fff3, 0x00790036, 0x00150064, 0xfffbfff1, + 0xfffafff3, 0x00790039, 0x00130061, 0xfffcfff1, + 0xfffbfff3, 0x0079003b, 0x0011005f, 0xfffdfff1, + 0xfffcfff2, 0x007a003e, 0x000f005d, 0xfffdfff1, + 0xfffdfff2, 0x007a0040, 0x000d005a, 0xfffffff1, + 0xfffefff2, 0x007b0043, 0x000b0058, 0xfffefff1, + 0x0000fff1, 0x007a0046, 0x00090055, 0x0000fff1, + 0x0001fff1, 0x007b0048, 0x00070053, 0x0000fff1, + 0x0002fff1, 0x007b004b, 0x00060050, 0x0000fff1 +}; + +static const u32 coef_lut_e_c_legacy[NB_COEF] = { + 0xfffa0001, 0x003f0010, 0x003f006d, 0xfffa0010, + 0xfffb0002, 0x00440011, 0x00440062, 0xfffa000e, + 0xfffb0001, 0x00460013, 0x00420062, 0xfffa000d, + 0xfffb0000, 0x00480014, 0x00410062, 0xfffa000c, + 0xfffb0001, 0x00490015, 0x003f0061, 0xfffb000b, + 0xfffb0000, 0x004b0017, 0x003d0061, 0xfffb000a, + 0xfffb0000, 0x004d0018, 0x003b0062, 0xfffb0008, + 0xfffcffff, 0x004f001a, 0x00390061, 0xfffb0007, + 0xfffc0000, 0x004f001c, 0x00380060, 0xfffb0006, + 0xfffcffff, 0x0052001d, 0x00360060, 0xfffb0005, + 0xfffdfffe, 0x0053001f, 0x00340060, 0xfffb0004, + 0xfffdfffe, 0x00540021, 0x0032005e, 0xfffc0004, + 0xfffeffff, 0x00550022, 0x0030005d, 0xfffc0003, + 0xfffeffff, 0x00560024, 0x002f005c, 0xfffc0002, + 0xfffffffd, 0x00580026, 0x002d005c, 0xfffc0001, + 0xfffffffd, 0x005a0027, 0x002b005c, 0xfffc0000, + 0x0000fffd, 0x005a0029, 0x0029005a, 0xfffd0000, + 0x0000fffc, 0x005c002b, 0x0027005a, 0xfffdffff, + 0x0001fffc, 0x005c002d, 0x00260058, 0xfffdffff, + 0x0002fffc, 0x005c002f, 0x00240056, 0xfffffffe, + 0x0003fffc, 0x005d0030, 0x00220055, 0xfffffffe, + 0x0004fffc, 0x005e0032, 0x00210054, 0xfffefffd, + 0x0004fffb, 0x00600034, 0x001f0053, 0xfffefffd, + 0x0005fffb, 0x00600036, 0x001d0052, 0xfffffffc, + 0x0006fffb, 0x00600038, 0x001c004f, 0x0000fffc, + 0x0007fffb, 0x00610039, 0x001a004f, 0xfffffffc, + 0x0008fffb, 0x0062003b, 0x0018004d, 0x0000fffb, + 0x000afffb, 0x0061003d, 0x0017004b, 0x0000fffb, + 0x000bfffb, 0x0061003f, 0x00150049, 0x0001fffb, + 0x000cfffa, 0x00620041, 0x00140048, 0x0000fffb, + 0x000dfffa, 0x00620042, 0x00130046, 0x0001fffb, + 0x000efffa, 0x00620044, 0x00110044, 0x0002fffb +}; + +static const u32 coef_lut_f_y_legacy[NB_COEF] = { + 0xfff6fff0, 0x00490012, 0x0049006e, 0xfff60012, + 0xfff7fff1, 0x004e0013, 0x00490068, 0xfff60010, + 0xfff7fff2, 0x004f0015, 0x00470067, 0xfff6000f, + 0xfff7fff5, 0x004f0017, 0x00450065, 0xfff6000e, + 0xfff8fff5, 0x00500018, 0x00440065, 0xfff6000c, + 0xfff8fff6, 0x0051001a, 0x00420064, 0xfff6000b, + 0xfff8fff6, 0x0052001c, 0x00400064, 0xfff6000a, + 0xfff9fff6, 0x0054001d, 0x003e0064, 0xfff60008, + 0xfff9fff8, 0x0054001f, 0x003c0063, 0xfff60007, + 0xfffafff8, 0x00550021, 0x003a0062, 0xfff60006, + 0xfffbfff7, 0x00560022, 0x00390062, 0xfff60005, + 0xfffbfff8, 0x00570024, 0x00370061, 0xfff60004, + 0xfffcfff8, 0x00580026, 0x00350060, 0xfff60003, + 0xfffdfff8, 0x00590028, 0x0033005f, 0xfff60002, + 0xfffdfff7, 0x005b002a, 0x0031005f, 0xfff60001, + 0xfffefff7, 0x005c002c, 0x002f005e, 0xfff60000, + 0xfffffff6, 0x005e002d, 0x002d005e, 0xfff6ffff, + 0x0000fff6, 0x005e002f, 0x002c005c, 0xfff7fffe, + 0x0001fff6, 0x005f0031, 0x002a005b, 0xfff7fffd, + 0x0002fff6, 0x005f0033, 0x00280059, 0xfff8fffd, + 0x0003fff6, 0x00600035, 0x00260058, 0xfff8fffc, + 0x0004fff6, 0x00610037, 0x00240057, 0xfff8fffb, + 0x0005fff6, 0x00620039, 0x00220056, 0xfff7fffb, + 0x0006fff6, 0x0062003a, 0x00210055, 0xfff8fffa, + 0x0007fff6, 0x0063003c, 0x001f0054, 0xfff8fff9, + 0x0008fff6, 0x0064003e, 0x001d0054, 0xfff6fff9, + 0x000afff6, 0x00640040, 0x001c0052, 0xfff6fff8, + 0x000bfff6, 0x00640042, 0x001a0051, 0xfff6fff8, + 0x000cfff6, 0x00650044, 0x00180050, 0xfff5fff8, + 0x000efff6, 0x00650045, 0x0017004f, 0xfff5fff7, + 0x000ffff6, 0x00670047, 0x0015004f, 0xfff2fff7, + 0x0010fff6, 0x00680049, 0x0013004e, 0xfff1fff7 +}; + +static const u32 coef_lut_f_c_legacy[NB_COEF] = { + 0x0000fffb, 0x003a001a, 0x003a005d, 0x0000001a, + 0x0001fffb, 0x003f001b, 0x00400051, 0x00000019, + 0x0001fffc, 0x0040001c, 0x003f0051, 0x00000017, + 0x0002fffb, 0x0042001d, 0x003e0051, 0xffff0016, + 0x0002fffb, 0x0043001e, 0x003d0051, 0xffff0015, + 0x0003fffc, 0x00430020, 0x003b0050, 0xffff0014, + 0x0003fffb, 0x00450021, 0x003a0051, 0xfffe0013, + 0x0004fffc, 0x00450022, 0x00390050, 0xfffe0012, + 0x0005fffc, 0x00460023, 0x0038004f, 0xfffe0011, + 0x0005fffb, 0x00480025, 0x00360050, 0xfffd0010, + 0x0006fffc, 0x00480026, 0x0035004f, 0xfffd000f, + 0x0006fffc, 0x00490027, 0x0034004f, 0xfffd000e, + 0x0007fffd, 0x00490028, 0x0033004e, 0xfffd000d, + 0x0008fffc, 0x004a002a, 0x0031004d, 0xfffd000d, + 0x0009fffd, 0x004a002b, 0x0030004d, 0xfffc000c, + 0x0009fffc, 0x004c002c, 0x002f004d, 0xfffc000b, + 0x000afffc, 0x004c002e, 0x002e004c, 0xfffc000a, + 0x000bfffc, 0x004d002f, 0x002c004c, 0xfffc0009, + 0x000cfffc, 0x004d0030, 0x002b004a, 0xfffd0009, + 0x000dfffd, 0x004d0031, 0x002a004a, 0xfffc0008, + 0x000dfffd, 0x004e0033, 0x00280049, 0xfffd0007, + 0x000efffd, 0x004f0034, 0x00270049, 0xfffc0006, + 0x000ffffd, 0x004f0035, 0x00260048, 0xfffc0006, + 0x0010fffd, 0x00500036, 0x00250048, 0xfffb0005, + 0x0011fffe, 0x004f0038, 0x00230046, 0xfffc0005, + 0x0012fffe, 0x00500039, 0x00220045, 0xfffc0004, + 0x0013fffe, 0x0051003a, 0x00210045, 0xfffb0003, + 0x0014ffff, 0x0050003b, 0x00200043, 0xfffc0003, + 0x0015ffff, 0x0051003d, 0x001e0043, 0xfffb0002, + 0x0016ffff, 0x0051003e, 0x001d0042, 0xfffb0002, + 0x00170000, 0x0051003f, 0x001c0040, 0xfffc0001, + 0x00190000, 0x00510040, 0x001b003f, 0xfffb0001 +}; + +#endif diff --git a/drivers/gpu/drm/sti/sti_layer.c b/drivers/gpu/drm/sti/sti_layer.c index 06a587c4f1bb810eeac9930577c700f4f7c8ecfd..480ec1c974e20c58b7dbd06efa4d988e8160a29e 100644 --- a/drivers/gpu/drm/sti/sti_layer.c +++ b/drivers/gpu/drm/sti/sti_layer.c @@ -11,7 +11,9 @@ #include #include "sti_compositor.h" +#include "sti_cursor.h" #include "sti_gdp.h" +#include "sti_hqvdp.h" #include "sti_layer.h" #include "sti_vid.h" @@ -32,6 +34,8 @@ const char *sti_layer_to_str(struct sti_layer *layer) return "VID1"; case STI_CURSOR: return "CURSOR"; + case STI_HQVDP_0: + return "HQVDP0"; default: return ""; } @@ -50,6 +54,12 @@ struct sti_layer *sti_layer_create(struct device *dev, int desc, case STI_VID: layer = sti_vid_create(dev); break; + case STI_CUR: + layer = sti_cursor_create(dev); + break; + case STI_VDP: + layer = sti_hqvdp_create(dev); + break; } if (!layer) { @@ -68,7 +78,9 @@ struct sti_layer *sti_layer_create(struct device *dev, int desc, return layer; } -int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb, +int sti_layer_prepare(struct sti_layer *layer, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, struct drm_display_mode *mode, int mixer_id, int dest_x, int dest_y, int dest_w, int dest_h, int src_x, int src_y, int src_w, int src_h) @@ -88,6 +100,7 @@ int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb, return 1; } + layer->crtc = crtc; layer->fb = fb; layer->mode = mode; layer->mixer_id = mixer_id; @@ -100,6 +113,7 @@ int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb, layer->src_w = src_w; layer->src_h = src_h; layer->format = fb->pixel_format; + layer->vaddr = cma_obj->vaddr; layer->paddr = cma_obj->paddr; for (i = 0; i < 4; i++) { layer->pitches[i] = fb->pitches[i]; diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h index 198c3774cc1250507d77ccbeb49725c3229c5ebd..ceff497f557ea5ec985d966a8710b6df2045949b 100644 --- a/drivers/gpu/drm/sti/sti_layer.h +++ b/drivers/gpu/drm/sti/sti_layer.h @@ -22,7 +22,8 @@ enum sti_layer_type { STI_GDP = 1 << STI_LAYER_TYPE_SHIFT, STI_VID = 2 << STI_LAYER_TYPE_SHIFT, STI_CUR = 3 << STI_LAYER_TYPE_SHIFT, - STI_BCK = 4 << STI_LAYER_TYPE_SHIFT + STI_BCK = 4 << STI_LAYER_TYPE_SHIFT, + STI_VDP = 5 << STI_LAYER_TYPE_SHIFT }; enum sti_layer_id_of_type { @@ -39,6 +40,7 @@ enum sti_layer_desc { STI_GDP_3 = STI_GDP | STI_ID_3, STI_VID_0 = STI_VID | STI_ID_0, STI_VID_1 = STI_VID | STI_ID_1, + STI_HQVDP_0 = STI_VDP | STI_ID_0, STI_CURSOR = STI_CUR, STI_BACK = STI_BCK }; @@ -67,6 +69,7 @@ struct sti_layer_funcs { * * @plane: drm plane it is bound to (if any) * @fb: drm fb it is bound to + * @crtc: crtc it is bound to * @mode: display mode * @desc: layer type & id * @device: driver device @@ -82,11 +85,13 @@ struct sti_layer_funcs { * @format: format * @pitches: pitch of 'planes' (eg: Y, U, V) * @offsets: offset of 'planes' + * @vaddr: virtual address of the input buffer * @paddr: physical address of the input buffer */ struct sti_layer { struct drm_plane plane; struct drm_framebuffer *fb; + struct drm_crtc *crtc; struct drm_display_mode *mode; enum sti_layer_desc desc; struct device *dev; @@ -102,12 +107,15 @@ struct sti_layer { uint32_t format; unsigned int pitches[4]; unsigned int offsets[4]; + void *vaddr; dma_addr_t paddr; }; struct sti_layer *sti_layer_create(struct device *dev, int desc, void __iomem *baseaddr); -int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb, +int sti_layer_prepare(struct sti_layer *layer, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, struct drm_display_mode *mode, int mixer_id, int dest_x, int dest_y, diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c index 79f369db9fb601cbeac3b2d93c0b4f28584a9237..13a4b84deab69104f82f3c341877e2e2aa6e3fbb 100644 --- a/drivers/gpu/drm/sti/sti_mixer.c +++ b/drivers/gpu/drm/sti/sti_mixer.c @@ -45,6 +45,7 @@ static const u32 mixerColorSpaceMatIdentity[] = { #define GAM_CTL_GDP1_MASK BIT(4) #define GAM_CTL_GDP2_MASK BIT(5) #define GAM_CTL_GDP3_MASK BIT(6) +#define GAM_CTL_CURSOR_MASK BIT(9) const char *sti_mixer_to_str(struct sti_mixer *mixer) { @@ -122,11 +123,15 @@ int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer) layer_id = GAM_DEPTH_GDP3_ID; break; case STI_VID_0: + case STI_HQVDP_0: layer_id = GAM_DEPTH_VID0_ID; break; case STI_VID_1: layer_id = GAM_DEPTH_VID1_ID; break; + case STI_CURSOR: + /* no need to set depth for cursor */ + return 0; default: DRM_ERROR("Unknown layer %d\n", layer->desc); return 1; @@ -185,9 +190,12 @@ static u32 sti_mixer_get_layer_mask(struct sti_layer *layer) case STI_GDP_3: return GAM_CTL_GDP3_MASK; case STI_VID_0: + case STI_HQVDP_0: return GAM_CTL_VID0_MASK; case STI_VID_1: return GAM_CTL_VID1_MASK; + case STI_CURSOR: + return GAM_CTL_CURSOR_MASK; default: return 0; } @@ -215,6 +223,15 @@ int sti_mixer_set_layer_status(struct sti_mixer *mixer, return 0; } +void sti_mixer_clear_all_layers(struct sti_mixer *mixer) +{ + u32 val; + + DRM_DEBUG_DRIVER("%s clear all layer\n", sti_mixer_to_str(mixer)); + val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL) & 0xFFFF0000; + sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val); +} + void sti_mixer_set_matrix(struct sti_mixer *mixer) { unsigned int i; diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h index 874372102e5237dae960acfb27b8c5a2030929a0..b97282182908f0cf77589e3e39b94c5b977a9bed 100644 --- a/drivers/gpu/drm/sti/sti_mixer.h +++ b/drivers/gpu/drm/sti/sti_mixer.h @@ -23,6 +23,7 @@ * @id: id of the mixer * @drm_crtc: crtc object link to the mixer * @pending_event: set if a flip event is pending on crtc + * @enabled: to know if the mixer is active or not */ struct sti_mixer { struct device *dev; @@ -30,6 +31,7 @@ struct sti_mixer { int id; struct drm_crtc drm_crtc; struct drm_pending_vblank_event *pending_event; + bool enabled; }; const char *sti_mixer_to_str(struct sti_mixer *mixer); @@ -39,6 +41,7 @@ struct sti_mixer *sti_mixer_create(struct device *dev, int id, int sti_mixer_set_layer_status(struct sti_mixer *mixer, struct sti_layer *layer, bool status); +void sti_mixer_clear_all_layers(struct sti_mixer *mixer); int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer); int sti_mixer_active_video_area(struct sti_mixer *mixer, struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c index b8afe490356aedf09bb361d295764020dfff5eac..cb924aa2b321b3e0b499876770b86f76eb78a751 100644 --- a/drivers/gpu/drm/sti/sti_tvout.c +++ b/drivers/gpu/drm/sti/sti_tvout.c @@ -16,6 +16,8 @@ #include #include +#include "sti_drm_crtc.h" + /* glue registers */ #define TVO_CSC_MAIN_M0 0x000 #define TVO_CSC_MAIN_M1 0x004 @@ -96,7 +98,7 @@ #define TVO_SYNC_HD_DCS_SHIFT 8 -#define ENCODER_MAIN_CRTC_MASK BIT(0) +#define ENCODER_CRTC_MASK (BIT(0) | BIT(1)) /* enum listing the supported output data format */ enum sti_tvout_video_out_type { @@ -149,14 +151,15 @@ static void tvout_write(struct sti_tvout *tvout, u32 val, int offset) * Set the clipping mode of a VIP * * @tvout: tvout structure + * @reg: register to set * @cr_r: * @y_g: * @cb_b: */ -static void tvout_vip_set_color_order(struct sti_tvout *tvout, +static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg, u32 cr_r, u32 y_g, u32 cb_b) { - u32 val = tvout_read(tvout, TVO_VIP_HDMI); + u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT); val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT); @@ -165,52 +168,58 @@ static void tvout_vip_set_color_order(struct sti_tvout *tvout, val |= y_g << TVO_VIP_REORDER_G_SHIFT; val |= cb_b << TVO_VIP_REORDER_B_SHIFT; - tvout_write(tvout, val, TVO_VIP_HDMI); + tvout_write(tvout, val, reg); } /** * Set the clipping mode of a VIP * * @tvout: tvout structure + * @reg: register to set * @range: clipping range */ -static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range) +static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range) { - u32 val = tvout_read(tvout, TVO_VIP_HDMI); + u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT); val |= range << TVO_VIP_CLIP_SHIFT; - tvout_write(tvout, val, TVO_VIP_HDMI); + tvout_write(tvout, val, reg); } /** * Set the rounded value of a VIP * * @tvout: tvout structure + * @reg: register to set * @rnd: rounded val per component */ -static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd) +static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd) { - u32 val = tvout_read(tvout, TVO_VIP_HDMI); + u32 val = tvout_read(tvout, reg); val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT); val |= rnd << TVO_VIP_RND_SHIFT; - tvout_write(tvout, val, TVO_VIP_HDMI); + tvout_write(tvout, val, reg); } /** * Select the VIP input * * @tvout: tvout structure + * @reg: register to set + * @main_path: main or auxiliary path + * @sel_input_logic_inverted: need to invert the logic * @sel_input: selected_input (main/aux + conv) */ static void tvout_vip_set_sel_input(struct sti_tvout *tvout, + int reg, bool main_path, bool sel_input_logic_inverted, enum sti_tvout_video_out_type video_out) { u32 sel_input; - u32 val = tvout_read(tvout, TVO_VIP_HDMI); + u32 val = tvout_read(tvout, reg); if (main_path) sel_input = TVO_VIP_SEL_INPUT_MAIN; @@ -232,22 +241,24 @@ static void tvout_vip_set_sel_input(struct sti_tvout *tvout, val &= ~TVO_VIP_SEL_INPUT_MASK; val |= sel_input; - tvout_write(tvout, val, TVO_VIP_HDMI); + tvout_write(tvout, val, reg); } /** * Select the input video signed or unsigned * * @tvout: tvout structure + * @reg: register to set * @in_vid_signed: used video input format */ -static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt) +static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, + int reg, u32 in_vid_fmt) { - u32 val = tvout_read(tvout, TVO_VIP_HDMI); + u32 val = tvout_read(tvout, reg); val &= ~TVO_IN_FMT_SIGNED; val |= in_vid_fmt; - tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT); + tvout_write(tvout, val, reg); } /** @@ -261,6 +272,7 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) { struct device_node *node = tvout->dev->of_node; bool sel_input_logic_inverted = false; + u32 tvo_in_vid_format; dev_dbg(tvout->dev, "%s\n", __func__); @@ -268,33 +280,36 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) DRM_DEBUG_DRIVER("main vip for hdmi\n"); /* select the input sync for hdmi = VTG set 1 */ tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); + tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; } else { DRM_DEBUG_DRIVER("aux vip for hdmi\n"); /* select the input sync for hdmi = VTG set 1 */ tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); + tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } /* set color channel order */ - tvout_vip_set_color_order(tvout, + tvout_vip_set_color_order(tvout, TVO_VIP_HDMI, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); /* set clipping mode (Limited range RGB/Y) */ - tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); + tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, + TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); /* set round mode (rounded to 8-bit per component) */ - tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED); + tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); if (of_device_is_compatible(node, "st,stih407-tvout")) { /* set input video format */ - tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT, - TVO_IN_FMT_SIGNED); + tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, + TVO_IN_FMT_SIGNED); sel_input_logic_inverted = true; } /* input selection */ - tvout_vip_set_sel_input(tvout, main_path, + tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path, sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); } @@ -309,48 +324,47 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) { struct device_node *node = tvout->dev->of_node; bool sel_input_logic_inverted = false; + u32 tvo_in_vid_format; + int val; dev_dbg(tvout->dev, "%s\n", __func__); - if (!main_path) { - DRM_ERROR("HD Analog on aux not implemented\n"); - return; + if (main_path) { + val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; + val |= TVO_SYNC_MAIN_VTG_SET_3; + tvout_write(tvout, val, TVO_HD_SYNC_SEL); + tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; + } else { + val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; + val |= TVO_SYNC_AUX_VTG_SET_3; + tvout_write(tvout, val, TVO_HD_SYNC_SEL); + tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; } - DRM_DEBUG_DRIVER("main vip for HDF\n"); - /* set color channel order */ - tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF, + tvout_vip_set_color_order(tvout, TVO_VIP_HDF, TVO_VIP_REORDER_CR_R_SEL, TVO_VIP_REORDER_Y_G_SEL, TVO_VIP_REORDER_CB_B_SEL); - /* set clipping mode (Limited range RGB/Y) */ - tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF, - TVO_VIP_CLIP_LIMITED_RANGE_CB_CR); + /* set clipping mode (EAV/SAV clipping) */ + tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV); /* set round mode (rounded to 10-bit per component) */ - tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); + tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); if (of_device_is_compatible(node, "st,stih407-tvout")) { /* set input video format */ - tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED); + tvout_vip_set_in_vid_fmt(tvout, + tvo_in_vid_format, TVO_IN_FMT_SIGNED); sel_input_logic_inverted = true; } /* Input selection */ - tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF, - main_path, + tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_YUV); - /* select the input sync for HD analog = VTG set 3 - * and HD DCS = VTG set 2 */ - tvout_write(tvout, - (TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT) - | TVO_SYNC_MAIN_VTG_SET_3, - TVO_HD_SYNC_SEL); - /* power up HD DAC */ tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); } @@ -392,7 +406,7 @@ static void sti_hda_encoder_commit(struct drm_encoder *encoder) { struct sti_tvout *tvout = to_sti_tvout(encoder); - tvout_hda_start(tvout, true); + tvout_hda_start(tvout, sti_drm_crtc_is_main(encoder->crtc)); } static void sti_hda_encoder_disable(struct drm_encoder *encoder) @@ -429,7 +443,7 @@ static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, drm_encoder = (struct drm_encoder *) encoder; - drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK; + drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; drm_encoder->possible_clones = 1 << 0; drm_encoder_init(dev, drm_encoder, @@ -444,7 +458,7 @@ static void sti_hdmi_encoder_commit(struct drm_encoder *encoder) { struct sti_tvout *tvout = to_sti_tvout(encoder); - tvout_hdmi_start(tvout, true); + tvout_hdmi_start(tvout, sti_drm_crtc_is_main(encoder->crtc)); } static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) @@ -478,7 +492,7 @@ static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev, drm_encoder = (struct drm_encoder *) encoder; - drm_encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK; + drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; drm_encoder->possible_clones = 1 << 1; drm_encoder_init(dev, drm_encoder, diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c index 740d6e347a62237fbe0145c97be95b059d7a1b16..9564f2568e2c01abaf975985b571431bdc9b3349 100644 --- a/drivers/gpu/drm/sti/sti_vtg.c +++ b/drivers/gpu/drm/sti/sti_vtg.c @@ -51,10 +51,19 @@ #define VTG_TOP_V_HD_3 0x010C #define VTG_BOT_V_HD_3 0x0110 +#define VTG_H_HD_4 0x0120 +#define VTG_TOP_V_VD_4 0x0124 +#define VTG_BOT_V_VD_4 0x0128 +#define VTG_TOP_V_HD_4 0x012c +#define VTG_BOT_V_HD_4 0x0130 + #define VTG_IRQ_BOTTOM BIT(0) #define VTG_IRQ_TOP BIT(1) #define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) +/* Delay introduced by the HDMI in nb of pixel */ +#define HDMI_DELAY (6) + /* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ #define AWG_DELAY_HD (-9) #define AWG_DELAY_ED (-8) @@ -133,10 +142,10 @@ static void vtg_set_mode(struct sti_vtg *vtg, writel(tmp, vtg->regs + VTG_VID_TFS); writel(tmp, vtg->regs + VTG_VID_BFS); - /* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */ - tmp = (mode->hsync_end - mode->hsync_start) << 16; + /* prepare VTG set 1 for HDMI */ + tmp = (mode->hsync_end - mode->hsync_start + HDMI_DELAY) << 16; + tmp |= HDMI_DELAY; writel(tmp, vtg->regs + VTG_H_HD_1); - writel(tmp, vtg->regs + VTG_H_HD_2); tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; tmp |= 1; @@ -146,6 +155,11 @@ static void vtg_set_mode(struct sti_vtg *vtg, writel(0, vtg->regs + VTG_BOT_V_HD_1); /* prepare VTG set 2 for for HD DCS */ + tmp = (mode->hsync_end - mode->hsync_start) << 16; + writel(tmp, vtg->regs + VTG_H_HD_2); + + tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; + tmp |= 1; writel(tmp, vtg->regs + VTG_TOP_V_VD_2); writel(tmp, vtg->regs + VTG_BOT_V_VD_2); writel(0, vtg->regs + VTG_TOP_V_HD_2); @@ -166,6 +180,17 @@ static void vtg_set_mode(struct sti_vtg *vtg, writel(tmp, vtg->regs + VTG_TOP_V_HD_3); writel(tmp, vtg->regs + VTG_BOT_V_HD_3); + /* Prepare VTG set 4 for DVO */ + tmp = (mode->hsync_end - mode->hsync_start) << 16; + writel(tmp, vtg->regs + VTG_H_HD_4); + + tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; + tmp |= 1; + writel(tmp, vtg->regs + VTG_TOP_V_VD_4); + writel(tmp, vtg->regs + VTG_BOT_V_VD_4); + writel(0, vtg->regs + VTG_TOP_V_HD_4); + writel(0, vtg->regs + VTG_BOT_V_HD_4); + /* mode */ writel(type, vtg->regs + VTG_MODE); }