imx-drm PRE/PRG support, deferred plane disabling, separate alpha support
- Initial support for the Prefetch Resolve Engine/Gasket on i.MX6QP, improving linear scanout buffer memory bandwidth utilization. This will in the future grow reordering support and allow direct scanout of Vivante tiled renderbuffers from the GPU. - Deferred plane disabling gets rid of some busy waiting in the atomic plane disable and crtc disable paths that lead to wait_for_vblank timeouts. - Add support for RGBA formats with a separate alpha plane, that can reduce memory bandwidth utilization for mostly transparent overlay planes by skipping color reads for completely transparent regions. - Allow moving an active overlay plane without enforcing a modeset. - Add 8-bit and 16-bit bayer formats to ipu_cpmem_set_image. - Set the base address in ipu_cpmem_set_image even for invalid formats to increase robustness against errors. - Use drm_plane_helper_check_state in plane atomic_check. - Some cleanup. -----BEGIN PGP SIGNATURE----- iQJLBAABCAA1FiEEBsBxhV1FaKwXuCOBUMKIHHCeYOsFAljLmwkXHHAuemFiZWxA cGVuZ3V0cm9uaXguZGUACgkQUMKIHHCeYOvhNQ/+NGLUB1S7qBpoRoNak/B23/Lj mNXSnJnqUO5iHea7bGEU0kA9Sv6IgE21YacB+Zvu5hVSu47OlO6c7LUni6VJkRx3 jXOOtZoR/67lWvgkZEEV4q2l6vVwBon/5xxuHXpGbhWu1652NdX7y/ueBSQaqva/ YJO1pm5ixoVoy/0FQwZ82+gi4BP4gdAqEwkxFOYUd8TluO9iUhXKvPuDH8D4DYbw jAQH5UpcoC7OEoxx56a4ivx9v/9JBfNnoZf3RpjxhTIMCxiSsJ0Cc+sM2g11INiR wh4q14pWRtZqbtSHhhOLipX4+XDpiUSrc13Mm+ThCB+JBJJZBTjQr9u+tfiQiHWq WLJ2NYQr50lUIVX5P/lwWevRrUYDBhQ0QTelO9Cjor1fAWfNWZl5ARyaIQZ4YREa duJswyqNVQxEpdBAe2dhabLmDhQ/JFh7OzKMNxvt+6P48XR3KMrHEHujZj38hEwR U2kZ+WNEQPztASMLQgBGIzagIQ/yQfLmJyiSNy/hnaUQg1h557Z1O2x5H99XOxvE jF9ub3iCwenYg8WuywS6vXAMZpm87+NYOFwyj/4KpPsQIM6n/DSPxf9drb5bIicU CpbOIz4qMBRHW8Bm6XlUqAMtleVUVye+yFQ9kYIcc042fuzwVzc7Fl8fH/BCbqr1 gGpo/07g21pMH12w90g= =dyZf -----END PGP SIGNATURE----- Merge tag 'imx-drm-next-2017-03-17' of git://git.pengutronix.de/git/pza/linux into drm-next imx-drm PRE/PRG support, deferred plane disabling, separate alpha support - Initial support for the Prefetch Resolve Engine/Gasket on i.MX6QP, improving linear scanout buffer memory bandwidth utilization. This will in the future grow reordering support and allow direct scanout of Vivante tiled renderbuffers from the GPU. - Deferred plane disabling gets rid of some busy waiting in the atomic plane disable and crtc disable paths that lead to wait_for_vblank timeouts. - Add support for RGBA formats with a separate alpha plane, that can reduce memory bandwidth utilization for mostly transparent overlay planes by skipping color reads for completely transparent regions. - Allow moving an active overlay plane without enforcing a modeset. - Add 8-bit and 16-bit bayer formats to ipu_cpmem_set_image. - Set the base address in ipu_cpmem_set_image even for invalid formats to increase robustness against errors. - Use drm_plane_helper_check_state in plane atomic_check. - Some cleanup. * tag 'imx-drm-next-2017-03-17' of git://git.pengutronix.de/git/pza/linux: (22 commits) drm/imx: Remove unneeded definition for structure imx_drm_component drm/imx: use PRG/PRE when possible drm/imx: enable/disable PRG on CRTC enable/disable gpu: ipu-v3: only set non-zero AXI ID for IC when PRG is absent gpu: ipu-v3: hook up PRG unit gpu: ipu-v3: document valid IPUv3 compatibles and extend for i.MX6 QuadPlus gpu: ipu-v3: add driver for Prefetch Resolve Gasket gpu: ipu-v3: add DT binding for the Prefetch Resolve Gasket gpu: ipu-v3: add driver for Prefetch Resolve Engine gpu: ipu-v3: add DT binding for the Prefetch Resolve Engine drm/imx: ipuv3-plane: add support for separate alpha planes drm/imx: extend drm_plane_state_to_eba for separate channel support gpu: ipu-v3: add support for separate alpha channels drm: add RGB formats with separate alpha plane drm/imx: add deferred plane disabling drm/imx: don't wait for vblank and stop calling cleanup_planes in commit_tail gpu: ipu-v3: add unsynchronised DP channel disabling gpu: ipu-v3: remove IRQ dance on DC channel disable gpu: ipu-cpmem: add bayer formats to ipu_cpmem_set_image gpu: ipu-cpmem: set image base address even for incorrect formats ...
This commit is contained in:
commit
33d5f513c6
|
@ -21,13 +21,19 @@ Freescale i.MX IPUv3
|
|||
====================
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<chip>-ipu"
|
||||
- compatible: Should be "fsl,<chip>-ipu" where <chip> is one of
|
||||
- imx51
|
||||
- imx53
|
||||
- imx6q
|
||||
- imx6qp
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- interrupts: Should contain sync interrupt and error interrupt,
|
||||
in this order.
|
||||
- resets: phandle pointing to the system reset controller and
|
||||
reset line index, see reset/fsl,imx-src.txt for details
|
||||
Additional required properties for fsl,imx6qp-ipu:
|
||||
- fsl,prg: phandle to prg node associated with this IPU instance
|
||||
Optional properties:
|
||||
- port@[0-3]: Port nodes with endpoint definitions as defined in
|
||||
Documentation/devicetree/bindings/media/video-interfaces.txt.
|
||||
|
@ -53,6 +59,57 @@ ipu: ipu@18000000 {
|
|||
};
|
||||
};
|
||||
|
||||
Freescale i.MX PRE (Prefetch Resolve Engine)
|
||||
============================================
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "fsl,imx6qp-pre"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- clocks : phandle to the PRE axi clock input, as described
|
||||
in Documentation/devicetree/bindings/clock/clock-bindings.txt and
|
||||
Documentation/devicetree/bindings/clock/imx6q-clock.txt.
|
||||
- clock-names: should be "axi"
|
||||
- interrupts: should contain the PRE interrupt
|
||||
- fsl,iram: phandle pointing to the mmio-sram device node, that should be
|
||||
used for the PRE SRAM double buffer.
|
||||
|
||||
example:
|
||||
|
||||
pre@21c8000 {
|
||||
compatible = "fsl,imx6qp-pre";
|
||||
reg = <0x021c8000 0x1000>;
|
||||
interrupts = <GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&clks IMX6QDL_CLK_PRE0>;
|
||||
clock-names = "axi";
|
||||
fsl,iram = <&ocram2>;
|
||||
};
|
||||
|
||||
Freescale i.MX PRG (Prefetch Resolve Gasket)
|
||||
============================================
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "fsl,imx6qp-prg"
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- clocks : phandles to the PRG ipg and axi clock inputs, as described
|
||||
in Documentation/devicetree/bindings/clock/clock-bindings.txt and
|
||||
Documentation/devicetree/bindings/clock/imx6q-clock.txt.
|
||||
- clock-names: should be "ipg" and "axi"
|
||||
- fsl,pres: phandles to the PRE units attached to this PRG, with the fixed
|
||||
PRE as the first entry and the muxable PREs following.
|
||||
|
||||
example:
|
||||
|
||||
prg@21cc000 {
|
||||
compatible = "fsl,imx6qp-prg";
|
||||
reg = <0x021cc000 0x1000>;
|
||||
clocks = <&clks IMX6QDL_CLK_PRG0_APB>,
|
||||
<&clks IMX6QDL_CLK_PRG0_AXI>;
|
||||
clock-names = "ipg", "axi";
|
||||
fsl,pres = <&pre1>, <&pre2>, <&pre3>;
|
||||
};
|
||||
|
||||
Parallel display support
|
||||
========================
|
||||
|
||||
|
|
|
@ -132,6 +132,8 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
|||
{ .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX8888, .depth = 24, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGR565_A8, .depth = 24, .num_planes = 2, .cpp = { 2, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX1010102, .depth = 30, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
|
@ -144,6 +146,12 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
|||
{ .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGB888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGR888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XRGB8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_RGBX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_BGRX8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_YUV410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 },
|
||||
{ .format = DRM_FORMAT_YVU410, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 4 },
|
||||
{ .format = DRM_FORMAT_YUV411, .depth = 0, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 4, .vsub = 1 },
|
||||
|
|
|
@ -30,14 +30,10 @@
|
|||
#include <video/imx-ipu-v3.h>
|
||||
|
||||
#include "imx-drm.h"
|
||||
#include "ipuv3-plane.h"
|
||||
|
||||
#define MAX_CRTC 4
|
||||
|
||||
struct imx_drm_component {
|
||||
struct device_node *of_node;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct imx_drm_device {
|
||||
struct drm_device *drm;
|
||||
unsigned int pipes;
|
||||
|
@ -109,6 +105,11 @@ static int imx_drm_atomic_check(struct drm_device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Assign PRG/PRE channels and check if all constrains are satisfied. */
|
||||
ret = ipu_planes_assign_pre(dev, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -122,6 +123,10 @@ static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
|
|||
static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = state->dev;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_state *old_plane_state;
|
||||
bool plane_disabling = false;
|
||||
int i;
|
||||
|
||||
drm_atomic_helper_commit_modeset_disables(dev, state);
|
||||
|
||||
|
@ -131,11 +136,20 @@ static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
|
|||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, state);
|
||||
|
||||
for_each_plane_in_state(state, plane, old_plane_state, i) {
|
||||
if (drm_atomic_plane_disabling(old_plane_state, plane->state))
|
||||
plane_disabling = true;
|
||||
}
|
||||
|
||||
if (plane_disabling) {
|
||||
drm_atomic_helper_wait_for_vblanks(dev, state);
|
||||
|
||||
for_each_plane_in_state(state, plane, old_plane_state, i)
|
||||
ipu_plane_disable_deferred(plane);
|
||||
|
||||
}
|
||||
|
||||
drm_atomic_helper_commit_hw_done(state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = {
|
||||
|
|
|
@ -39,4 +39,7 @@ int imx_drm_encoder_parse_of(struct drm_device *drm,
|
|||
void imx_drm_connector_destroy(struct drm_connector *connector);
|
||||
void imx_drm_encoder_destroy(struct drm_encoder *encoder);
|
||||
|
||||
int ipu_planes_assign_pre(struct drm_device *dev,
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
#endif /* _IMX_DRM_H_ */
|
||||
|
|
|
@ -55,11 +55,32 @@ static void ipu_crtc_enable(struct drm_crtc *crtc)
|
|||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
|
||||
|
||||
ipu_prg_enable(ipu);
|
||||
ipu_dc_enable(ipu);
|
||||
ipu_dc_enable_channel(ipu_crtc->dc);
|
||||
ipu_di_enable(ipu_crtc->di);
|
||||
}
|
||||
|
||||
static void ipu_crtc_disable_planes(struct ipu_crtc *ipu_crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
bool disable_partial = false;
|
||||
bool disable_full = false;
|
||||
struct drm_plane *plane;
|
||||
|
||||
drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) {
|
||||
if (plane == &ipu_crtc->plane[0]->base)
|
||||
disable_full = true;
|
||||
if (&ipu_crtc->plane[1] && plane == &ipu_crtc->plane[1]->base)
|
||||
disable_partial = true;
|
||||
}
|
||||
|
||||
if (disable_partial)
|
||||
ipu_plane_disable(ipu_crtc->plane[1], true);
|
||||
if (disable_full)
|
||||
ipu_plane_disable(ipu_crtc->plane[0], false);
|
||||
}
|
||||
|
||||
static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
|
@ -73,8 +94,9 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
|
|||
* attached IDMACs will be left in undefined state, possibly hanging
|
||||
* the IPU or even system.
|
||||
*/
|
||||
drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
|
||||
ipu_crtc_disable_planes(ipu_crtc, old_crtc_state);
|
||||
ipu_dc_disable(ipu);
|
||||
ipu_prg_disable(ipu);
|
||||
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->event) {
|
||||
|
|
|
@ -23,6 +23,17 @@
|
|||
#include "video/imx-ipu-v3.h"
|
||||
#include "ipuv3-plane.h"
|
||||
|
||||
struct ipu_plane_state {
|
||||
struct drm_plane_state base;
|
||||
bool use_pre;
|
||||
};
|
||||
|
||||
static inline struct ipu_plane_state *
|
||||
to_ipu_plane_state(struct drm_plane_state *p)
|
||||
{
|
||||
return container_of(p, struct ipu_plane_state, base);
|
||||
}
|
||||
|
||||
static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
|
||||
{
|
||||
return container_of(p, struct ipu_plane, base);
|
||||
|
@ -57,6 +68,12 @@ static const uint32_t ipu_plane_formats[] = {
|
|||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV16,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_RGB565_A8,
|
||||
DRM_FORMAT_BGR565_A8,
|
||||
DRM_FORMAT_RGB888_A8,
|
||||
DRM_FORMAT_BGR888_A8,
|
||||
DRM_FORMAT_RGBX8888_A8,
|
||||
DRM_FORMAT_BGRX8888_A8,
|
||||
};
|
||||
|
||||
int ipu_plane_irq(struct ipu_plane *ipu_plane)
|
||||
|
@ -66,18 +83,18 @@ int ipu_plane_irq(struct ipu_plane *ipu_plane)
|
|||
}
|
||||
|
||||
static inline unsigned long
|
||||
drm_plane_state_to_eba(struct drm_plane_state *state)
|
||||
drm_plane_state_to_eba(struct drm_plane_state *state, int plane)
|
||||
{
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
int x = state->src_x >> 16;
|
||||
int y = state->src_y >> 16;
|
||||
int x = state->src.x1 >> 16;
|
||||
int y = state->src.y1 >> 16;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, plane);
|
||||
BUG_ON(!cma_obj);
|
||||
|
||||
return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y +
|
||||
fb->format->cpp[0] * x;
|
||||
return cma_obj->paddr + fb->offsets[plane] + fb->pitches[plane] * y +
|
||||
fb->format->cpp[plane] * x;
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
|
@ -85,9 +102,9 @@ drm_plane_state_to_ubo(struct drm_plane_state *state)
|
|||
{
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
unsigned long eba = drm_plane_state_to_eba(state);
|
||||
int x = state->src_x >> 16;
|
||||
int y = state->src_y >> 16;
|
||||
unsigned long eba = drm_plane_state_to_eba(state, 0);
|
||||
int x = state->src.x1 >> 16;
|
||||
int y = state->src.y1 >> 16;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 1);
|
||||
BUG_ON(!cma_obj);
|
||||
|
@ -104,9 +121,9 @@ drm_plane_state_to_vbo(struct drm_plane_state *state)
|
|||
{
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
unsigned long eba = drm_plane_state_to_eba(state);
|
||||
int x = state->src_x >> 16;
|
||||
int y = state->src_y >> 16;
|
||||
unsigned long eba = drm_plane_state_to_eba(state, 0);
|
||||
int x = state->src.x1 >> 16;
|
||||
int y = state->src.y1 >> 16;
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 2);
|
||||
BUG_ON(!cma_obj);
|
||||
|
@ -126,11 +143,14 @@ void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
|
|||
ipu_dmfc_put(ipu_plane->dmfc);
|
||||
if (!IS_ERR_OR_NULL(ipu_plane->ipu_ch))
|
||||
ipu_idmac_put(ipu_plane->ipu_ch);
|
||||
if (!IS_ERR_OR_NULL(ipu_plane->alpha_ch))
|
||||
ipu_idmac_put(ipu_plane->alpha_ch);
|
||||
}
|
||||
|
||||
int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
|
||||
{
|
||||
int ret;
|
||||
int alpha_ch;
|
||||
|
||||
ipu_plane->ipu_ch = ipu_idmac_get(ipu_plane->ipu, ipu_plane->dma);
|
||||
if (IS_ERR(ipu_plane->ipu_ch)) {
|
||||
|
@ -139,6 +159,17 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
|
|||
return ret;
|
||||
}
|
||||
|
||||
alpha_ch = ipu_channel_alpha_channel(ipu_plane->dma);
|
||||
if (alpha_ch >= 0) {
|
||||
ipu_plane->alpha_ch = ipu_idmac_get(ipu_plane->ipu, alpha_ch);
|
||||
if (IS_ERR(ipu_plane->alpha_ch)) {
|
||||
ret = PTR_ERR(ipu_plane->alpha_ch);
|
||||
DRM_ERROR("failed to get alpha idmac channel %d: %d\n",
|
||||
alpha_ch, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ipu_plane->dmfc = ipu_dmfc_get(ipu_plane->ipu, ipu_plane->dma);
|
||||
if (IS_ERR(ipu_plane->dmfc)) {
|
||||
ret = PTR_ERR(ipu_plane->dmfc);
|
||||
|
@ -162,34 +193,62 @@ err_out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool ipu_plane_separate_alpha(struct ipu_plane *ipu_plane)
|
||||
{
|
||||
switch (ipu_plane->base.state->fb->format->format) {
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipu_plane_enable(struct ipu_plane *ipu_plane)
|
||||
{
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_enable(ipu_plane->ipu);
|
||||
ipu_dmfc_enable_channel(ipu_plane->dmfc);
|
||||
ipu_idmac_enable_channel(ipu_plane->ipu_ch);
|
||||
if (ipu_plane_separate_alpha(ipu_plane))
|
||||
ipu_idmac_enable_channel(ipu_plane->alpha_ch);
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_enable_channel(ipu_plane->dp);
|
||||
}
|
||||
|
||||
static int ipu_disable_plane(struct drm_plane *plane)
|
||||
void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
|
||||
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_disable_channel(ipu_plane->dp);
|
||||
if (ipu_plane->dp && disable_dp_channel)
|
||||
ipu_dp_disable_channel(ipu_plane->dp, false);
|
||||
ipu_idmac_disable_channel(ipu_plane->ipu_ch);
|
||||
if (ipu_plane->alpha_ch)
|
||||
ipu_idmac_disable_channel(ipu_plane->alpha_ch);
|
||||
ipu_dmfc_disable_channel(ipu_plane->dmfc);
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_disable(ipu_plane->ipu);
|
||||
|
||||
return 0;
|
||||
if (ipu_prg_present(ipu_plane->ipu))
|
||||
ipu_prg_channel_disable(ipu_plane->ipu_ch);
|
||||
}
|
||||
|
||||
void ipu_plane_disable_deferred(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
||||
if (ipu_plane->disabling) {
|
||||
ipu_plane->disabling = false;
|
||||
ipu_plane_disable(ipu_plane, false);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_plane_disable_deferred);
|
||||
|
||||
static void ipu_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
@ -200,13 +259,56 @@ static void ipu_plane_destroy(struct drm_plane *plane)
|
|||
kfree(ipu_plane);
|
||||
}
|
||||
|
||||
void ipu_plane_state_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane_state *ipu_state;
|
||||
|
||||
if (plane->state) {
|
||||
ipu_state = to_ipu_plane_state(plane->state);
|
||||
__drm_atomic_helper_plane_destroy_state(plane->state);
|
||||
kfree(ipu_state);
|
||||
}
|
||||
|
||||
ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL);
|
||||
|
||||
if (ipu_state) {
|
||||
ipu_state->base.plane = plane;
|
||||
ipu_state->base.rotation = DRM_ROTATE_0;
|
||||
}
|
||||
|
||||
plane->state = &ipu_state->base;
|
||||
}
|
||||
|
||||
struct drm_plane_state *ipu_plane_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane_state *state;
|
||||
|
||||
if (WARN_ON(!plane->state))
|
||||
return NULL;
|
||||
|
||||
state = kmalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (state)
|
||||
__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
|
||||
|
||||
return &state->base;
|
||||
}
|
||||
|
||||
void ipu_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct ipu_plane_state *ipu_state = to_ipu_plane_state(state);
|
||||
|
||||
__drm_atomic_helper_plane_destroy_state(state);
|
||||
kfree(ipu_state);
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs ipu_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = ipu_plane_destroy,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
.reset = ipu_plane_state_reset,
|
||||
.atomic_duplicate_state = ipu_plane_duplicate_state,
|
||||
.atomic_destroy_state = ipu_plane_destroy_state,
|
||||
};
|
||||
|
||||
static int ipu_plane_atomic_check(struct drm_plane *plane,
|
||||
|
@ -217,8 +319,11 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
struct device *dev = plane->dev->dev;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_framebuffer *old_fb = old_state->fb;
|
||||
unsigned long eba, ubo, vbo, old_ubo, old_vbo;
|
||||
unsigned long eba, ubo, vbo, old_ubo, old_vbo, alpha_eba;
|
||||
bool can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY);
|
||||
struct drm_rect clip;
|
||||
int hsub, vsub;
|
||||
int ret;
|
||||
|
||||
/* Ok to disable */
|
||||
if (!fb)
|
||||
|
@ -232,44 +337,35 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
clip.x1 = 0;
|
||||
clip.y1 = 0;
|
||||
clip.x2 = crtc_state->adjusted_mode.hdisplay;
|
||||
clip.y2 = crtc_state->adjusted_mode.vdisplay;
|
||||
ret = drm_plane_helper_check_state(state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
can_position, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* CRTC should be enabled */
|
||||
if (!crtc_state->enable)
|
||||
return -EINVAL;
|
||||
|
||||
/* no scaling */
|
||||
if (state->src_w >> 16 != state->crtc_w ||
|
||||
state->src_h >> 16 != state->crtc_h)
|
||||
return -EINVAL;
|
||||
|
||||
switch (plane->type) {
|
||||
case DRM_PLANE_TYPE_PRIMARY:
|
||||
/* full plane doesn't support partial off screen */
|
||||
if (state->crtc_x || state->crtc_y ||
|
||||
state->crtc_w != crtc_state->adjusted_mode.hdisplay ||
|
||||
state->crtc_h != crtc_state->adjusted_mode.vdisplay)
|
||||
return -EINVAL;
|
||||
|
||||
/* full plane minimum width is 13 pixels */
|
||||
if (state->crtc_w < 13)
|
||||
if (drm_rect_width(&state->dst) < 13)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case DRM_PLANE_TYPE_OVERLAY:
|
||||
if (state->crtc_x < 0 || state->crtc_y < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->crtc_x + state->crtc_w >
|
||||
crtc_state->adjusted_mode.hdisplay)
|
||||
return -EINVAL;
|
||||
if (state->crtc_y + state->crtc_h >
|
||||
crtc_state->adjusted_mode.vdisplay)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Unsupported plane type\n");
|
||||
dev_warn(dev, "Unsupported plane type %d\n", plane->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state->crtc_h < 2)
|
||||
if (drm_rect_height(&state->dst) < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
@ -279,12 +375,13 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
* callback. The planes will be reenabled in plane's ->atomic_update
|
||||
* callback.
|
||||
*/
|
||||
if (old_fb && (state->src_w != old_state->src_w ||
|
||||
state->src_h != old_state->src_h ||
|
||||
fb->format != old_fb->format))
|
||||
if (old_fb &&
|
||||
(drm_rect_width(&state->dst) != drm_rect_width(&old_state->dst) ||
|
||||
drm_rect_height(&state->dst) != drm_rect_height(&old_state->dst) ||
|
||||
fb->format != old_fb->format))
|
||||
crtc_state->mode_changed = true;
|
||||
|
||||
eba = drm_plane_state_to_eba(state);
|
||||
eba = drm_plane_state_to_eba(state, 0);
|
||||
|
||||
if (eba & 0x7)
|
||||
return -EINVAL;
|
||||
|
@ -350,9 +447,26 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
*/
|
||||
hsub = drm_format_horz_chroma_subsampling(fb->format->format);
|
||||
vsub = drm_format_vert_chroma_subsampling(fb->format->format);
|
||||
if (((state->src_x >> 16) & (hsub - 1)) ||
|
||||
((state->src_y >> 16) & (vsub - 1)))
|
||||
if (((state->src.x1 >> 16) & (hsub - 1)) ||
|
||||
((state->src.y1 >> 16) & (vsub - 1)))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
alpha_eba = drm_plane_state_to_eba(state, 1);
|
||||
if (alpha_eba & 0x7)
|
||||
return -EINVAL;
|
||||
|
||||
if (fb->pitches[1] < 1 || fb->pitches[1] > 16384)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_fb && old_fb->pitches[1] != fb->pitches[1])
|
||||
crtc_state->mode_changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -361,7 +475,25 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
|
|||
static void ipu_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
ipu_disable_plane(plane);
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_disable_channel(ipu_plane->dp, true);
|
||||
ipu_plane->disabling = true;
|
||||
}
|
||||
|
||||
static int ipu_chan_assign_axi_id(int ipu_chan)
|
||||
{
|
||||
switch (ipu_chan) {
|
||||
case IPUV3_CHANNEL_MEM_BG_SYNC:
|
||||
return 1;
|
||||
case IPUV3_CHANNEL_MEM_FG_SYNC:
|
||||
return 2;
|
||||
case IPUV3_CHANNEL_MEM_DC_SYNC:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
|
@ -369,18 +501,47 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct ipu_plane_state *ipu_state = to_ipu_plane_state(state);
|
||||
struct drm_crtc_state *crtc_state = state->crtc->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_rect *dst = &state->dst;
|
||||
unsigned long eba, ubo, vbo;
|
||||
unsigned long alpha_eba = 0;
|
||||
enum ipu_color_space ics;
|
||||
unsigned int axi_id = 0;
|
||||
int active;
|
||||
|
||||
eba = drm_plane_state_to_eba(state);
|
||||
if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_FG)
|
||||
ipu_dp_set_window_pos(ipu_plane->dp, dst->x1, dst->y1);
|
||||
|
||||
eba = drm_plane_state_to_eba(state, 0);
|
||||
|
||||
/*
|
||||
* Configure PRG channel and attached PRE, this changes the EBA to an
|
||||
* internal SRAM location.
|
||||
*/
|
||||
if (ipu_state->use_pre) {
|
||||
axi_id = ipu_chan_assign_axi_id(ipu_plane->dma);
|
||||
ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id,
|
||||
drm_rect_width(&state->src) >> 16,
|
||||
drm_rect_height(&state->src) >> 16,
|
||||
state->fb->pitches[0],
|
||||
state->fb->format->format, &eba);
|
||||
}
|
||||
|
||||
if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) {
|
||||
/* nothing to do if PRE is used */
|
||||
if (ipu_state->use_pre)
|
||||
return;
|
||||
active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
|
||||
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
|
||||
ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
|
||||
if (ipu_plane_separate_alpha(ipu_plane)) {
|
||||
active = ipu_idmac_get_current_buffer(ipu_plane->alpha_ch);
|
||||
ipu_cpmem_set_buffer(ipu_plane->alpha_ch, !active,
|
||||
alpha_eba);
|
||||
ipu_idmac_select_buffer(ipu_plane->alpha_ch, !active);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -395,8 +556,6 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
ics = ipu_drm_fourcc_to_colorspace(state->fb->format->format);
|
||||
ipu_dp_setup_channel(ipu_plane->dp, ics,
|
||||
IPUV3_COLORSPACE_UNKNOWN);
|
||||
ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x,
|
||||
state->crtc_y);
|
||||
/* Enable local alpha on partial plane */
|
||||
switch (state->fb->format->format) {
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
|
@ -408,6 +567,12 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
|
||||
break;
|
||||
default:
|
||||
|
@ -416,15 +581,17 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
}
|
||||
}
|
||||
|
||||
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, state->crtc_w);
|
||||
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, drm_rect_width(dst));
|
||||
|
||||
ipu_cpmem_zero(ipu_plane->ipu_ch);
|
||||
ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16,
|
||||
state->src_h >> 16);
|
||||
ipu_cpmem_set_resolution(ipu_plane->ipu_ch,
|
||||
drm_rect_width(&state->src) >> 16,
|
||||
drm_rect_height(&state->src) >> 16);
|
||||
ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->format->format);
|
||||
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
|
||||
ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
|
||||
ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]);
|
||||
ipu_cpmem_set_axi_id(ipu_plane->ipu_ch, axi_id);
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
|
@ -444,7 +611,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
|
||||
dev_dbg(ipu_plane->base.dev->dev,
|
||||
"phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo,
|
||||
state->src_x >> 16, state->src_y >> 16);
|
||||
state->src.x1 >> 16, state->src.y1 >> 16);
|
||||
break;
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV16:
|
||||
|
@ -455,11 +622,37 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
|
|||
|
||||
dev_dbg(ipu_plane->base.dev->dev,
|
||||
"phy = %lu %lu, x = %d, y = %d", eba, ubo,
|
||||
state->src_x >> 16, state->src_y >> 16);
|
||||
state->src.x1 >> 16, state->src.y1 >> 16);
|
||||
break;
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
alpha_eba = drm_plane_state_to_eba(state, 1);
|
||||
|
||||
dev_dbg(ipu_plane->base.dev->dev, "phys = %lu %lu, x = %d, y = %d",
|
||||
eba, alpha_eba, state->src.x1 >> 16, state->src.y1 >> 16);
|
||||
|
||||
ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16);
|
||||
|
||||
ipu_cpmem_zero(ipu_plane->alpha_ch);
|
||||
ipu_cpmem_set_resolution(ipu_plane->alpha_ch,
|
||||
drm_rect_width(&state->src) >> 16,
|
||||
drm_rect_height(&state->src) >> 16);
|
||||
ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
|
||||
ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
|
||||
ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1);
|
||||
ipu_cpmem_set_stride(ipu_plane->alpha_ch,
|
||||
state->fb->pitches[1]);
|
||||
ipu_cpmem_set_burstsize(ipu_plane->alpha_ch, 16);
|
||||
ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 0, alpha_eba);
|
||||
ipu_cpmem_set_buffer(ipu_plane->alpha_ch, 1, alpha_eba);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d",
|
||||
eba, state->src_x >> 16, state->src_y >> 16);
|
||||
eba, state->src.x1 >> 16, state->src.y1 >> 16);
|
||||
break;
|
||||
}
|
||||
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
|
||||
|
@ -474,6 +667,35 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
|
|||
.atomic_update = ipu_plane_atomic_update,
|
||||
};
|
||||
|
||||
int ipu_planes_assign_pre(struct drm_device *dev,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_plane *plane;
|
||||
int available_pres = ipu_prg_max_active_channels();
|
||||
int i;
|
||||
|
||||
for_each_plane_in_state(state, plane, plane_state, i) {
|
||||
struct ipu_plane_state *ipu_state =
|
||||
to_ipu_plane_state(plane_state);
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
||||
if (ipu_prg_present(ipu_plane->ipu) && available_pres &&
|
||||
plane_state->fb &&
|
||||
ipu_prg_format_supported(ipu_plane->ipu,
|
||||
plane_state->fb->format->format,
|
||||
plane_state->fb->modifier)) {
|
||||
ipu_state->use_pre = true;
|
||||
available_pres--;
|
||||
} else {
|
||||
ipu_state->use_pre = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_planes_assign_pre);
|
||||
|
||||
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
||||
int dma, int dp, unsigned int possible_crtcs,
|
||||
enum drm_plane_type type)
|
||||
|
|
|
@ -18,11 +18,14 @@ struct ipu_plane {
|
|||
|
||||
struct ipu_soc *ipu;
|
||||
struct ipuv3_channel *ipu_ch;
|
||||
struct ipuv3_channel *alpha_ch;
|
||||
struct dmfc_channel *dmfc;
|
||||
struct ipu_dp *dp;
|
||||
|
||||
int dma;
|
||||
int dp_flow;
|
||||
|
||||
bool disabling;
|
||||
};
|
||||
|
||||
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
||||
|
@ -42,4 +45,7 @@ void ipu_plane_put_resources(struct ipu_plane *plane);
|
|||
|
||||
int ipu_plane_irq(struct ipu_plane *plane);
|
||||
|
||||
void ipu_plane_disable(struct ipu_plane *ipu_plane, bool disable_dp_channel);
|
||||
void ipu_plane_disable_deferred(struct drm_plane *plane);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
|
|||
|
||||
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
|
||||
ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \
|
||||
ipu-smfc.o ipu-vdi.o
|
||||
ipu-pre.o ipu-prg.o ipu-smfc.o ipu-vdi.o
|
||||
|
|
|
@ -51,15 +51,17 @@ int ipu_get_num(struct ipu_soc *ipu)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_get_num);
|
||||
|
||||
void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
|
||||
void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = ipu_cm_read(ipu, IPU_SRM_PRI2);
|
||||
val |= 0x8;
|
||||
val &= ~DP_S_SRM_MODE_MASK;
|
||||
val |= sync ? DP_S_SRM_MODE_NEXT_FRAME :
|
||||
DP_S_SRM_MODE_NOW;
|
||||
ipu_cm_write(ipu, val, IPU_SRM_PRI2);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
|
||||
EXPORT_SYMBOL_GPL(ipu_srm_dp_update);
|
||||
|
||||
enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
|
||||
{
|
||||
|
@ -81,6 +83,12 @@ enum ipu_color_space ipu_drm_fourcc_to_colorspace(u32 drm_fourcc)
|
|||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
return IPUV3_COLORSPACE_RGB;
|
||||
case DRM_FORMAT_YUYV:
|
||||
case DRM_FORMAT_UYVY:
|
||||
|
@ -931,6 +939,7 @@ static const struct of_device_id imx_ipu_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
|
||||
{ .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
|
||||
{ .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
|
||||
{ .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
|
||||
|
@ -1390,11 +1399,19 @@ static int ipu_probe(struct platform_device *pdev)
|
|||
if (!ipu)
|
||||
return -ENODEV;
|
||||
|
||||
ipu->id = of_alias_get_id(np, "ipu");
|
||||
|
||||
if (of_device_is_compatible(np, "fsl,imx6qp-ipu")) {
|
||||
ipu->prg_priv = ipu_prg_lookup_by_phandle(&pdev->dev,
|
||||
"fsl,prg", ipu->id);
|
||||
if (!ipu->prg_priv)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++)
|
||||
ipu->channel[i].ipu = ipu;
|
||||
ipu->devtype = devtype;
|
||||
ipu->ipu_type = devtype->type;
|
||||
ipu->id = of_alias_get_id(np, "ipu");
|
||||
|
||||
spin_lock_init(&ipu->lock);
|
||||
mutex_init(&ipu->channel_lock);
|
||||
|
@ -1520,7 +1537,23 @@ static struct platform_driver imx_ipu_driver = {
|
|||
.remove = ipu_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(imx_ipu_driver);
|
||||
static struct platform_driver * const drivers[] = {
|
||||
&ipu_pre_drv,
|
||||
&ipu_prg_drv,
|
||||
&imx_ipu_driver,
|
||||
};
|
||||
|
||||
static int __init imx_ipu_init(void)
|
||||
{
|
||||
return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
|
||||
}
|
||||
module_init(imx_ipu_init);
|
||||
|
||||
static void __exit imx_ipu_exit(void)
|
||||
{
|
||||
platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
|
||||
}
|
||||
module_exit(imx_ipu_exit);
|
||||
|
||||
MODULE_ALIAS("platform:imx-ipuv3");
|
||||
MODULE_DESCRIPTION("i.MX IPU v3 driver");
|
||||
|
|
|
@ -537,6 +537,43 @@ static const struct ipu_rgb def_bgra_16 = {
|
|||
#define UV2_OFFSET(pix, x, y) ((pix->width * pix->height) + \
|
||||
(pix->width * y) + (x))
|
||||
|
||||
#define NUM_ALPHA_CHANNELS 7
|
||||
|
||||
/* See Table 37-12. Alpha channels mapping. */
|
||||
static int ipu_channel_albm(int ch_num)
|
||||
{
|
||||
switch (ch_num) {
|
||||
case IPUV3_CHANNEL_G_MEM_IC_PRP_VF: return 0;
|
||||
case IPUV3_CHANNEL_G_MEM_IC_PP: return 1;
|
||||
case IPUV3_CHANNEL_MEM_FG_SYNC: return 2;
|
||||
case IPUV3_CHANNEL_MEM_FG_ASYNC: return 3;
|
||||
case IPUV3_CHANNEL_MEM_BG_SYNC: return 4;
|
||||
case IPUV3_CHANNEL_MEM_BG_ASYNC: return 5;
|
||||
case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB: return 6;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipu_cpmem_set_separate_alpha(struct ipuv3_channel *ch)
|
||||
{
|
||||
struct ipu_soc *ipu = ch->ipu;
|
||||
int albm;
|
||||
u32 val;
|
||||
|
||||
albm = ipu_channel_albm(ch->num);
|
||||
if (albm < 0)
|
||||
return;
|
||||
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_ALU, 1);
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_ALBM, albm);
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_CRE, 1);
|
||||
|
||||
val = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA);
|
||||
val |= BIT(ch->num);
|
||||
ipu_idmac_write(ipu, val, IDMAC_SEP_ALPHA);
|
||||
}
|
||||
|
||||
int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc)
|
||||
{
|
||||
switch (drm_fourcc) {
|
||||
|
@ -599,22 +636,28 @@ int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc)
|
|||
break;
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_rgbx_32);
|
||||
break;
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_bgrx_32);
|
||||
break;
|
||||
case DRM_FORMAT_BGR888:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_bgr_24);
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_rgb_24);
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_rgb_16);
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
ipu_cpmem_set_format_rgb(ch, &def_bgr_16);
|
||||
break;
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
|
@ -636,6 +679,20 @@ int ipu_cpmem_set_fmt(struct ipuv3_channel *ch, u32 drm_fourcc)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (drm_fourcc) {
|
||||
case DRM_FORMAT_RGB565_A8:
|
||||
case DRM_FORMAT_BGR565_A8:
|
||||
case DRM_FORMAT_RGB888_A8:
|
||||
case DRM_FORMAT_BGR888_A8:
|
||||
case DRM_FORMAT_RGBX8888_A8:
|
||||
case DRM_FORMAT_BGRX8888_A8:
|
||||
ipu_ch_param_write_field(ch, IPU_FIELD_WID3, 7);
|
||||
ipu_cpmem_set_separate_alpha(ch);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
|
||||
|
@ -644,6 +701,7 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
|
|||
{
|
||||
struct v4l2_pix_format *pix = &image->pix;
|
||||
int offset, u_offset, v_offset;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("%s: resolution: %dx%d stride: %d\n",
|
||||
__func__, pix->width, pix->height,
|
||||
|
@ -719,14 +777,30 @@ int ipu_cpmem_set_image(struct ipuv3_channel *ch, struct ipu_image *image)
|
|||
offset = image->rect.left * 3 +
|
||||
image->rect.top * pix->bytesperline;
|
||||
break;
|
||||
case V4L2_PIX_FMT_SBGGR8:
|
||||
case V4L2_PIX_FMT_SGBRG8:
|
||||
case V4L2_PIX_FMT_SGRBG8:
|
||||
case V4L2_PIX_FMT_SRGGB8:
|
||||
offset = image->rect.left + image->rect.top * pix->bytesperline;
|
||||
break;
|
||||
case V4L2_PIX_FMT_SBGGR16:
|
||||
case V4L2_PIX_FMT_SGBRG16:
|
||||
case V4L2_PIX_FMT_SGRBG16:
|
||||
case V4L2_PIX_FMT_SRGGB16:
|
||||
offset = image->rect.left * 2 +
|
||||
image->rect.top * pix->bytesperline;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
/* This should not happen */
|
||||
WARN_ON(1);
|
||||
offset = 0;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
ipu_cpmem_set_buffer(ch, 0, image->phys0 + offset);
|
||||
ipu_cpmem_set_buffer(ch, 1, image->phys1 + offset);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
|
||||
|
||||
|
|
|
@ -112,8 +112,6 @@ struct ipu_dc_priv {
|
|||
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
|
||||
struct mutex mutex;
|
||||
struct completion comp;
|
||||
int dc_irq;
|
||||
int dp_irq;
|
||||
int use_count;
|
||||
};
|
||||
|
||||
|
@ -262,47 +260,13 @@ void ipu_dc_enable_channel(struct ipu_dc *dc)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
|
||||
|
||||
static irqreturn_t dc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct ipu_dc *dc = dev_id;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(dc->base + DC_WR_CH_CONF);
|
||||
reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
|
||||
writel(reg, dc->base + DC_WR_CH_CONF);
|
||||
|
||||
/* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */
|
||||
|
||||
complete(&dc->priv->comp);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void ipu_dc_disable_channel(struct ipu_dc *dc)
|
||||
{
|
||||
struct ipu_dc_priv *priv = dc->priv;
|
||||
int irq;
|
||||
unsigned long ret;
|
||||
u32 val;
|
||||
|
||||
/* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
|
||||
if (dc->chno == 1)
|
||||
irq = priv->dc_irq;
|
||||
else if (dc->chno == 5)
|
||||
irq = priv->dp_irq;
|
||||
else
|
||||
return;
|
||||
|
||||
init_completion(&priv->comp);
|
||||
enable_irq(irq);
|
||||
ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50));
|
||||
disable_irq(irq);
|
||||
if (ret == 0) {
|
||||
dev_warn(priv->dev, "DC stop timeout after 50 ms\n");
|
||||
|
||||
val = readl(dc->base + DC_WR_CH_CONF);
|
||||
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
|
||||
writel(val, dc->base + DC_WR_CH_CONF);
|
||||
}
|
||||
val = readl(dc->base + DC_WR_CH_CONF);
|
||||
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
|
||||
writel(val, dc->base + DC_WR_CH_CONF);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
|
||||
|
||||
|
@ -389,7 +353,7 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
|
|||
struct ipu_dc_priv *priv;
|
||||
static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
|
||||
0x78, 0, 0x94, 0xb4};
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
@ -410,23 +374,6 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
|
|||
priv->channels[i].base = priv->dc_reg + channel_offsets[i];
|
||||
}
|
||||
|
||||
priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1);
|
||||
if (!priv->dc_irq)
|
||||
return -EINVAL;
|
||||
ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL,
|
||||
&priv->channels[1]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
disable_irq(priv->dc_irq);
|
||||
priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END);
|
||||
if (!priv->dp_irq)
|
||||
return -EINVAL;
|
||||
ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL,
|
||||
&priv->channels[5]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
disable_irq(priv->dp_irq);
|
||||
|
||||
writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
|
||||
DC_WR_CH_CONF_PROG_DI_ID,
|
||||
priv->channels[1].base + DC_WR_CH_CONF);
|
||||
|
|
|
@ -112,7 +112,7 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
|
|||
writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
|
||||
}
|
||||
|
||||
ipu_srm_dp_sync_update(priv->ipu);
|
||||
ipu_srm_dp_update(priv->ipu, true);
|
||||
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
|
@ -127,7 +127,7 @@ int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
|
|||
|
||||
writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
|
||||
|
||||
ipu_srm_dp_sync_update(priv->ipu);
|
||||
ipu_srm_dp_update(priv->ipu, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ int ipu_dp_setup_channel(struct ipu_dp *dp,
|
|||
flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
|
||||
}
|
||||
|
||||
ipu_srm_dp_sync_update(priv->ipu);
|
||||
ipu_srm_dp_update(priv->ipu, true);
|
||||
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
|
@ -247,7 +247,7 @@ int ipu_dp_enable_channel(struct ipu_dp *dp)
|
|||
reg |= DP_COM_CONF_FG_EN;
|
||||
writel(reg, flow->base + DP_COM_CONF);
|
||||
|
||||
ipu_srm_dp_sync_update(priv->ipu);
|
||||
ipu_srm_dp_update(priv->ipu, true);
|
||||
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
|
@ -255,7 +255,7 @@ int ipu_dp_enable_channel(struct ipu_dp *dp)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
|
||||
|
||||
void ipu_dp_disable_channel(struct ipu_dp *dp)
|
||||
void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync)
|
||||
{
|
||||
struct ipu_flow *flow = to_flow(dp);
|
||||
struct ipu_dp_priv *priv = flow->priv;
|
||||
|
@ -275,10 +275,7 @@ void ipu_dp_disable_channel(struct ipu_dp *dp)
|
|||
writel(reg, flow->base + DP_COM_CONF);
|
||||
|
||||
writel(0, flow->base + DP_FG_POS);
|
||||
ipu_srm_dp_sync_update(priv->ipu);
|
||||
|
||||
if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
|
||||
ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);
|
||||
ipu_srm_dp_update(priv->ipu, sync);
|
||||
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
|
|
@ -671,7 +671,12 @@ static void init_idmac_channel(struct ipu_image_convert_ctx *ctx,
|
|||
ipu_ic_task_idma_init(chan->ic, channel, width, height,
|
||||
burst_size, rot_mode);
|
||||
|
||||
ipu_cpmem_set_axi_id(channel, 1);
|
||||
/*
|
||||
* Setting a non-zero AXI ID collides with the PRG AXI snooping, so
|
||||
* only do this when there is no PRG present.
|
||||
*/
|
||||
if (!channel->ipu->prg_priv)
|
||||
ipu_cpmem_set_axi_id(channel, 1);
|
||||
|
||||
ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Lucas Stach, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <video/imx-ipu-v3.h>
|
||||
|
||||
#include "ipu-prv.h"
|
||||
|
||||
#define IPU_PRE_MAX_WIDTH 2048
|
||||
#define IPU_PRE_NUM_SCANLINES 8
|
||||
|
||||
#define IPU_PRE_CTRL 0x000
|
||||
#define IPU_PRE_CTRL_SET 0x004
|
||||
#define IPU_PRE_CTRL_ENABLE (1 << 0)
|
||||
#define IPU_PRE_CTRL_BLOCK_EN (1 << 1)
|
||||
#define IPU_PRE_CTRL_BLOCK_16 (1 << 2)
|
||||
#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4)
|
||||
#define IPU_PRE_CTRL_VFLIP (1 << 5)
|
||||
#define IPU_PRE_CTRL_SO (1 << 6)
|
||||
#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7)
|
||||
#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8)
|
||||
#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9)
|
||||
#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11)
|
||||
#define IPU_PRE_CTRL_EN_REPEAT (1 << 28)
|
||||
#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29)
|
||||
#define IPU_PRE_CTRL_CLKGATE (1 << 30)
|
||||
#define IPU_PRE_CTRL_SFTRST (1 << 31)
|
||||
|
||||
#define IPU_PRE_CUR_BUF 0x030
|
||||
|
||||
#define IPU_PRE_NEXT_BUF 0x040
|
||||
|
||||
#define IPU_PRE_TPR_CTRL 0x070
|
||||
#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0)
|
||||
#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff
|
||||
|
||||
#define IPU_PRE_PREFETCH_ENG_CTRL 0x080
|
||||
#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14)
|
||||
#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15)
|
||||
|
||||
#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0
|
||||
#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0)
|
||||
#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16)
|
||||
|
||||
#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0
|
||||
#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0)
|
||||
#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16)
|
||||
|
||||
#define IPU_PRE_STORE_ENG_CTRL 0x110
|
||||
#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0)
|
||||
#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1)
|
||||
#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
|
||||
|
||||
#define IPU_PRE_STORE_ENG_SIZE 0x130
|
||||
#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0)
|
||||
#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)
|
||||
|
||||
#define IPU_PRE_STORE_ENG_PITCH 0x140
|
||||
#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0)
|
||||
|
||||
#define IPU_PRE_STORE_ENG_ADDR 0x150
|
||||
|
||||
struct ipu_pre {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *regs;
|
||||
struct clk *clk_axi;
|
||||
struct gen_pool *iram;
|
||||
|
||||
dma_addr_t buffer_paddr;
|
||||
void *buffer_virt;
|
||||
bool in_use;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ipu_pre_list_mutex);
|
||||
static LIST_HEAD(ipu_pre_list);
|
||||
static int available_pres;
|
||||
|
||||
int ipu_pre_get_available_count(void)
|
||||
{
|
||||
return available_pres;
|
||||
}
|
||||
|
||||
struct ipu_pre *
|
||||
ipu_pre_lookup_by_phandle(struct device *dev, const char *name, int index)
|
||||
{
|
||||
struct device_node *pre_node = of_parse_phandle(dev->of_node,
|
||||
name, index);
|
||||
struct ipu_pre *pre;
|
||||
|
||||
mutex_lock(&ipu_pre_list_mutex);
|
||||
list_for_each_entry(pre, &ipu_pre_list, list) {
|
||||
if (pre_node == pre->dev->of_node) {
|
||||
mutex_unlock(&ipu_pre_list_mutex);
|
||||
device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE);
|
||||
return pre;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ipu_pre_list_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ipu_pre_get(struct ipu_pre *pre)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (pre->in_use)
|
||||
return -EBUSY;
|
||||
|
||||
clk_prepare_enable(pre->clk_axi);
|
||||
|
||||
/* first get the engine out of reset and remove clock gating */
|
||||
writel(0, pre->regs + IPU_PRE_CTRL);
|
||||
|
||||
/* init defaults that should be applied to all streams */
|
||||
val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |
|
||||
IPU_PRE_CTRL_HANDSHAKE_EN |
|
||||
IPU_PRE_CTRL_TPR_REST_SEL |
|
||||
IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE;
|
||||
writel(val, pre->regs + IPU_PRE_CTRL);
|
||||
|
||||
pre->in_use = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipu_pre_put(struct ipu_pre *pre)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE;
|
||||
writel(val, pre->regs + IPU_PRE_CTRL);
|
||||
|
||||
clk_disable_unprepare(pre->clk_axi);
|
||||
|
||||
pre->in_use = false;
|
||||
}
|
||||
|
||||
void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
|
||||
unsigned int height, unsigned int stride, u32 format,
|
||||
unsigned int bufaddr)
|
||||
{
|
||||
const struct drm_format_info *info = drm_format_info(format);
|
||||
u32 active_bpp = info->cpp[0] >> 1;
|
||||
u32 val;
|
||||
|
||||
writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
|
||||
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
||||
|
||||
val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
|
||||
IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
|
||||
IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |
|
||||
IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |
|
||||
IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;
|
||||
writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);
|
||||
|
||||
val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |
|
||||
IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);
|
||||
writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);
|
||||
|
||||
val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);
|
||||
writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);
|
||||
|
||||
val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) |
|
||||
IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |
|
||||
IPU_PRE_STORE_ENG_CTRL_STORE_EN;
|
||||
writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);
|
||||
|
||||
val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |
|
||||
IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);
|
||||
writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);
|
||||
|
||||
val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);
|
||||
writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);
|
||||
|
||||
writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR);
|
||||
|
||||
val = readl(pre->regs + IPU_PRE_CTRL);
|
||||
val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE |
|
||||
IPU_PRE_CTRL_SDW_UPDATE;
|
||||
writel(val, pre->regs + IPU_PRE_CTRL);
|
||||
}
|
||||
|
||||
void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr)
|
||||
{
|
||||
writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
|
||||
writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
|
||||
}
|
||||
|
||||
u32 ipu_pre_get_baddr(struct ipu_pre *pre)
|
||||
{
|
||||
return (u32)pre->buffer_paddr;
|
||||
}
|
||||
|
||||
static int ipu_pre_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct ipu_pre *pre;
|
||||
|
||||
pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);
|
||||
if (!pre)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pre->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(pre->regs))
|
||||
return PTR_ERR(pre->regs);
|
||||
|
||||
pre->clk_axi = devm_clk_get(dev, "axi");
|
||||
if (IS_ERR(pre->clk_axi))
|
||||
return PTR_ERR(pre->clk_axi);
|
||||
|
||||
pre->iram = of_gen_pool_get(dev->of_node, "fsl,iram", 0);
|
||||
if (!pre->iram)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* Allocate IRAM buffer with maximum size. This could be made dynamic,
|
||||
* but as there is no other user of this IRAM region and we can fit all
|
||||
* max sized buffers into it, there is no need yet.
|
||||
*/
|
||||
pre->buffer_virt = gen_pool_dma_alloc(pre->iram, IPU_PRE_MAX_WIDTH *
|
||||
IPU_PRE_NUM_SCANLINES * 4,
|
||||
&pre->buffer_paddr);
|
||||
if (!pre->buffer_virt)
|
||||
return -ENOMEM;
|
||||
|
||||
pre->dev = dev;
|
||||
platform_set_drvdata(pdev, pre);
|
||||
mutex_lock(&ipu_pre_list_mutex);
|
||||
list_add(&pre->list, &ipu_pre_list);
|
||||
available_pres++;
|
||||
mutex_unlock(&ipu_pre_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipu_pre_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ipu_pre *pre = platform_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&ipu_pre_list_mutex);
|
||||
list_del(&pre->list);
|
||||
available_pres--;
|
||||
mutex_unlock(&ipu_pre_list_mutex);
|
||||
|
||||
if (pre->buffer_virt)
|
||||
gen_pool_free(pre->iram, (unsigned long)pre->buffer_virt,
|
||||
IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ipu_pre_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6qp-pre", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
struct platform_driver ipu_pre_drv = {
|
||||
.probe = ipu_pre_probe,
|
||||
.remove = ipu_pre_remove,
|
||||
.driver = {
|
||||
.name = "imx-ipu-pre",
|
||||
.of_match_table = ipu_pre_dt_ids,
|
||||
},
|
||||
};
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2017 Lucas Stach, Pengutronix
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <video/imx-ipu-v3.h>
|
||||
|
||||
#include "ipu-prv.h"
|
||||
|
||||
#define IPU_PRG_CTL 0x00
|
||||
#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i))
|
||||
#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3
|
||||
#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2)
|
||||
#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i))
|
||||
#define IPU_PRG_CTL_SO(i) (1 << (16 + i))
|
||||
#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i))
|
||||
#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i))
|
||||
#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i))
|
||||
#define IPU_PRG_CTL_SOFTRST (1 << 30)
|
||||
#define IPU_PRG_CTL_SHADOW_EN (1 << 31)
|
||||
|
||||
#define IPU_PRG_STATUS 0x04
|
||||
#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2))
|
||||
#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2))
|
||||
|
||||
#define IPU_PRG_QOS 0x08
|
||||
#define IPU_PRG_QOS_ARID_MASK 0xf
|
||||
#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4)
|
||||
|
||||
#define IPU_PRG_REG_UPDATE 0x0c
|
||||
#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0)
|
||||
|
||||
#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4)
|
||||
#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff
|
||||
|
||||
#define IPU_PRG_CROP_LINE 0x1c
|
||||
|
||||
#define IPU_PRG_THD 0x20
|
||||
|
||||
#define IPU_PRG_BADDR(i) (0x24 + i * 0x4)
|
||||
|
||||
#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4)
|
||||
|
||||
#define IPU_PRG_ILO(i) (0x3c + i * 0x4)
|
||||
|
||||
#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4)
|
||||
#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff
|
||||
#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0
|
||||
#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff
|
||||
#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16
|
||||
|
||||
struct ipu_prg_channel {
|
||||
bool enabled;
|
||||
int used_pre;
|
||||
};
|
||||
|
||||
struct ipu_prg {
|
||||
struct list_head list;
|
||||
struct device *dev;
|
||||
int id;
|
||||
|
||||
void __iomem *regs;
|
||||
struct clk *clk_ipg, *clk_axi;
|
||||
struct regmap *iomuxc_gpr;
|
||||
struct ipu_pre *pres[3];
|
||||
|
||||
struct ipu_prg_channel chan[3];
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ipu_prg_list_mutex);
|
||||
static LIST_HEAD(ipu_prg_list);
|
||||
|
||||
struct ipu_prg *
|
||||
ipu_prg_lookup_by_phandle(struct device *dev, const char *name, int ipu_id)
|
||||
{
|
||||
struct device_node *prg_node = of_parse_phandle(dev->of_node,
|
||||
name, 0);
|
||||
struct ipu_prg *prg;
|
||||
|
||||
mutex_lock(&ipu_prg_list_mutex);
|
||||
list_for_each_entry(prg, &ipu_prg_list, list) {
|
||||
if (prg_node == prg->dev->of_node) {
|
||||
mutex_unlock(&ipu_prg_list_mutex);
|
||||
device_link_add(dev, prg->dev, DL_FLAG_AUTOREMOVE);
|
||||
prg->id = ipu_id;
|
||||
return prg;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ipu_prg_list_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ipu_prg_max_active_channels(void)
|
||||
{
|
||||
return ipu_pre_get_available_count();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_max_active_channels);
|
||||
|
||||
bool ipu_prg_present(struct ipu_soc *ipu)
|
||||
{
|
||||
if (ipu->prg_priv)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_present);
|
||||
|
||||
bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
|
||||
uint64_t modifier)
|
||||
{
|
||||
const struct drm_format_info *info = drm_format_info(format);
|
||||
|
||||
if (info->num_planes != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_format_supported);
|
||||
|
||||
int ipu_prg_enable(struct ipu_soc *ipu)
|
||||
{
|
||||
struct ipu_prg *prg = ipu->prg_priv;
|
||||
int ret;
|
||||
|
||||
if (!prg)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(prg->clk_axi);
|
||||
if (ret)
|
||||
goto fail_disable_ipg;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_disable_ipg:
|
||||
clk_disable_unprepare(prg->clk_ipg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_enable);
|
||||
|
||||
void ipu_prg_disable(struct ipu_soc *ipu)
|
||||
{
|
||||
struct ipu_prg *prg = ipu->prg_priv;
|
||||
|
||||
if (!prg)
|
||||
return;
|
||||
|
||||
clk_disable_unprepare(prg->clk_axi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_disable);
|
||||
|
||||
/*
|
||||
* The channel configuartion functions below are not thread safe, as they
|
||||
* must be only called from the atomic commit path in the DRM driver, which
|
||||
* is properly serialized.
|
||||
*/
|
||||
static int ipu_prg_ipu_to_prg_chan(int ipu_chan)
|
||||
{
|
||||
/*
|
||||
* This isn't clearly documented in the RM, but IPU to PRG channel
|
||||
* assignment is fixed, as only with this mapping the control signals
|
||||
* match up.
|
||||
*/
|
||||
switch (ipu_chan) {
|
||||
case IPUV3_CHANNEL_MEM_BG_SYNC:
|
||||
return 0;
|
||||
case IPUV3_CHANNEL_MEM_FG_SYNC:
|
||||
return 1;
|
||||
case IPUV3_CHANNEL_MEM_DC_SYNC:
|
||||
return 2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
/* channel 0 is special as it is hardwired to one of the PREs */
|
||||
if (prg_chan == 0) {
|
||||
ret = ipu_pre_get(prg->pres[0]);
|
||||
if (ret)
|
||||
goto fail;
|
||||
prg->chan[prg_chan].used_pre = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 1; i < 3; i++) {
|
||||
ret = ipu_pre_get(prg->pres[i]);
|
||||
if (!ret) {
|
||||
u32 val, mux;
|
||||
int shift;
|
||||
|
||||
prg->chan[prg_chan].used_pre = i;
|
||||
|
||||
/* configure the PRE to PRG channel mux */
|
||||
shift = (i == 1) ? 12 : 14;
|
||||
mux = (prg->id << 1) | (prg_chan - 1);
|
||||
regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
|
||||
0x3 << shift, mux << shift);
|
||||
|
||||
/* check other mux, must not point to same channel */
|
||||
shift = (i == 1) ? 14 : 12;
|
||||
regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val);
|
||||
if (((val >> shift) & 0x3) == mux) {
|
||||
regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
|
||||
0x3 << shift,
|
||||
(mux ^ 0x1) << shift);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan)
|
||||
{
|
||||
struct ipu_prg_channel *chan = &prg->chan[prg_chan];
|
||||
|
||||
ipu_pre_put(prg->pres[chan->used_pre]);
|
||||
chan->used_pre = -1;
|
||||
}
|
||||
|
||||
void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan)
|
||||
{
|
||||
int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
|
||||
struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
|
||||
struct ipu_prg_channel *chan = &prg->chan[prg_chan];
|
||||
u32 val;
|
||||
|
||||
if (!chan->enabled || prg_chan < 0)
|
||||
return;
|
||||
|
||||
clk_prepare_enable(prg->clk_ipg);
|
||||
|
||||
val = readl(prg->regs + IPU_PRG_CTL);
|
||||
val |= IPU_PRG_CTL_BYPASS(prg_chan);
|
||||
writel(val, prg->regs + IPU_PRG_CTL);
|
||||
|
||||
val = IPU_PRG_REG_UPDATE_REG_UPDATE;
|
||||
writel(val, prg->regs + IPU_PRG_REG_UPDATE);
|
||||
|
||||
clk_disable_unprepare(prg->clk_ipg);
|
||||
|
||||
ipu_prg_put_pre(prg, prg_chan);
|
||||
|
||||
chan->enabled = false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_channel_disable);
|
||||
|
||||
int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
|
||||
unsigned int axi_id, unsigned int width,
|
||||
unsigned int height, unsigned int stride,
|
||||
u32 format, unsigned long *eba)
|
||||
{
|
||||
int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
|
||||
struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
|
||||
struct ipu_prg_channel *chan = &prg->chan[prg_chan];
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (prg_chan < 0)
|
||||
return prg_chan;
|
||||
|
||||
if (chan->enabled) {
|
||||
ipu_pre_update(prg->pres[chan->used_pre], *eba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ipu_prg_get_pre(prg, prg_chan);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ipu_pre_configure(prg->pres[chan->used_pre],
|
||||
width, height, stride, format, *eba);
|
||||
|
||||
|
||||
ret = clk_prepare_enable(prg->clk_ipg);
|
||||
if (ret) {
|
||||
ipu_prg_put_pre(prg, prg_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK;
|
||||
writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan));
|
||||
|
||||
val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) <<
|
||||
IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) |
|
||||
((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) <<
|
||||
IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT);
|
||||
writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan));
|
||||
|
||||
val = ipu_pre_get_baddr(prg->pres[chan->used_pre]);
|
||||
*eba = val;
|
||||
writel(val, prg->regs + IPU_PRG_BADDR(prg_chan));
|
||||
|
||||
val = readl(prg->regs + IPU_PRG_CTL);
|
||||
/* counter load enable */
|
||||
val |= IPU_PRG_CTL_CNT_LOAD_EN(prg_chan);
|
||||
/* config AXI ID */
|
||||
val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK <<
|
||||
IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan));
|
||||
val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id);
|
||||
/* enable channel */
|
||||
val &= ~IPU_PRG_CTL_BYPASS(prg_chan);
|
||||
writel(val, prg->regs + IPU_PRG_CTL);
|
||||
|
||||
val = IPU_PRG_REG_UPDATE_REG_UPDATE;
|
||||
writel(val, prg->regs + IPU_PRG_REG_UPDATE);
|
||||
|
||||
clk_disable_unprepare(prg->clk_ipg);
|
||||
|
||||
chan->enabled = true;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_prg_channel_configure);
|
||||
|
||||
static int ipu_prg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct ipu_prg *prg;
|
||||
u32 val;
|
||||
int i, ret;
|
||||
|
||||
prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
|
||||
if (!prg)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
prg->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(prg->regs))
|
||||
return PTR_ERR(prg->regs);
|
||||
|
||||
|
||||
prg->clk_ipg = devm_clk_get(dev, "ipg");
|
||||
if (IS_ERR(prg->clk_ipg))
|
||||
return PTR_ERR(prg->clk_ipg);
|
||||
|
||||
prg->clk_axi = devm_clk_get(dev, "axi");
|
||||
if (IS_ERR(prg->clk_axi))
|
||||
return PTR_ERR(prg->clk_axi);
|
||||
|
||||
prg->iomuxc_gpr =
|
||||
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
||||
if (IS_ERR(prg->iomuxc_gpr))
|
||||
return PTR_ERR(prg->iomuxc_gpr);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
prg->pres[i] = ipu_pre_lookup_by_phandle(dev, "fsl,pres", i);
|
||||
if (!prg->pres[i])
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(prg->clk_ipg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* init to free running mode */
|
||||
val = readl(prg->regs + IPU_PRG_CTL);
|
||||
val |= IPU_PRG_CTL_SHADOW_EN;
|
||||
writel(val, prg->regs + IPU_PRG_CTL);
|
||||
|
||||
/* disable address threshold */
|
||||
writel(0xffffffff, prg->regs + IPU_PRG_THD);
|
||||
|
||||
clk_disable_unprepare(prg->clk_ipg);
|
||||
|
||||
prg->dev = dev;
|
||||
platform_set_drvdata(pdev, prg);
|
||||
mutex_lock(&ipu_prg_list_mutex);
|
||||
list_add(&prg->list, &ipu_prg_list);
|
||||
mutex_unlock(&ipu_prg_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipu_prg_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ipu_prg *prg = platform_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&ipu_prg_list_mutex);
|
||||
list_del(&prg->list);
|
||||
mutex_unlock(&ipu_prg_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ipu_prg_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6qp-prg", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
struct platform_driver ipu_prg_drv = {
|
||||
.probe = ipu_prg_probe,
|
||||
.remove = ipu_prg_remove,
|
||||
.driver = {
|
||||
.name = "imx-ipu-prg",
|
||||
.of_match_table = ipu_prg_dt_ids,
|
||||
},
|
||||
};
|
|
@ -75,6 +75,11 @@ struct ipu_soc;
|
|||
#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n))
|
||||
#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
|
||||
|
||||
/* SRM_PRI2 */
|
||||
#define DP_S_SRM_MODE_MASK (0x3 << 3)
|
||||
#define DP_S_SRM_MODE_NOW (0x3 << 3)
|
||||
#define DP_S_SRM_MODE_NEXT_FRAME (0x1 << 3)
|
||||
|
||||
/* FS_PROC_FLOW1 */
|
||||
#define FS_PRPENC_ROT_SRC_SEL_MASK (0xf << 0)
|
||||
#define FS_PRPENC_ROT_SRC_SEL_ENC (0x7 << 0)
|
||||
|
@ -168,6 +173,8 @@ struct ipu_ic_priv;
|
|||
struct ipu_vdi;
|
||||
struct ipu_image_convert_priv;
|
||||
struct ipu_smfc_priv;
|
||||
struct ipu_pre;
|
||||
struct ipu_prg;
|
||||
|
||||
struct ipu_devtype;
|
||||
|
||||
|
@ -202,6 +209,7 @@ struct ipu_soc {
|
|||
struct ipu_vdi *vdi_priv;
|
||||
struct ipu_image_convert_priv *image_convert_priv;
|
||||
struct ipu_smfc_priv *smfc_priv;
|
||||
struct ipu_prg *prg_priv;
|
||||
};
|
||||
|
||||
static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
|
||||
|
@ -215,7 +223,7 @@ static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
|
|||
writel(value, ipu->idmac_reg + offset);
|
||||
}
|
||||
|
||||
void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
|
||||
void ipu_srm_dp_update(struct ipu_soc *ipu, bool sync);
|
||||
|
||||
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
|
||||
int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
|
||||
|
@ -259,4 +267,21 @@ void ipu_cpmem_exit(struct ipu_soc *ipu);
|
|||
int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
|
||||
void ipu_smfc_exit(struct ipu_soc *ipu);
|
||||
|
||||
struct ipu_pre *ipu_pre_lookup_by_phandle(struct device *dev, const char *name,
|
||||
int index);
|
||||
int ipu_pre_get_available_count(void);
|
||||
int ipu_pre_get(struct ipu_pre *pre);
|
||||
void ipu_pre_put(struct ipu_pre *pre);
|
||||
u32 ipu_pre_get_baddr(struct ipu_pre *pre);
|
||||
void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
|
||||
unsigned int height,
|
||||
unsigned int stride, u32 format, unsigned int bufaddr);
|
||||
void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
|
||||
|
||||
struct ipu_prg *ipu_prg_lookup_by_phandle(struct device *dev, const char *name,
|
||||
int ipu_id);
|
||||
|
||||
extern struct platform_driver ipu_pre_drv;
|
||||
extern struct platform_driver ipu_prg_drv;
|
||||
|
||||
#endif /* __IPU_PRV_H__ */
|
||||
|
|
|
@ -113,6 +113,20 @@ extern "C" {
|
|||
|
||||
#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
|
||||
|
||||
/*
|
||||
* 2 plane RGB + A
|
||||
* index 0 = RGB plane, same format as the corresponding non _A8 format has
|
||||
* index 1 = A plane, [7:0] A
|
||||
*/
|
||||
#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8')
|
||||
#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8')
|
||||
#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8')
|
||||
#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8')
|
||||
#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8')
|
||||
#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8')
|
||||
#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8')
|
||||
#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8')
|
||||
|
||||
/*
|
||||
* 2 plane YCbCr
|
||||
* index 0 = Y plane, [7:0] Y
|
||||
|
|
|
@ -161,6 +161,28 @@ enum ipu_channel_irq {
|
|||
#define IPUV3_CHANNEL_MEM_BG_ASYNC_ALPHA 52
|
||||
#define IPUV3_NUM_CHANNELS 64
|
||||
|
||||
static inline int ipu_channel_alpha_channel(int ch_num)
|
||||
{
|
||||
switch (ch_num) {
|
||||
case IPUV3_CHANNEL_G_MEM_IC_PRP_VF:
|
||||
return IPUV3_CHANNEL_G_MEM_IC_PRP_VF_ALPHA;
|
||||
case IPUV3_CHANNEL_G_MEM_IC_PP:
|
||||
return IPUV3_CHANNEL_G_MEM_IC_PP_ALPHA;
|
||||
case IPUV3_CHANNEL_MEM_FG_SYNC:
|
||||
return IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA;
|
||||
case IPUV3_CHANNEL_MEM_FG_ASYNC:
|
||||
return IPUV3_CHANNEL_MEM_FG_ASYNC_ALPHA;
|
||||
case IPUV3_CHANNEL_MEM_BG_SYNC:
|
||||
return IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA;
|
||||
case IPUV3_CHANNEL_MEM_BG_ASYNC:
|
||||
return IPUV3_CHANNEL_MEM_BG_ASYNC_ALPHA;
|
||||
case IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB:
|
||||
return IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB_ALPHA;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int ipu_map_irq(struct ipu_soc *ipu, int irq);
|
||||
int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
|
||||
enum ipu_channel_irq irq);
|
||||
|
@ -300,7 +322,7 @@ struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
|
|||
void ipu_dp_put(struct ipu_dp *);
|
||||
int ipu_dp_enable(struct ipu_soc *ipu);
|
||||
int ipu_dp_enable_channel(struct ipu_dp *dp);
|
||||
void ipu_dp_disable_channel(struct ipu_dp *dp);
|
||||
void ipu_dp_disable_channel(struct ipu_dp *dp, bool sync);
|
||||
void ipu_dp_disable(struct ipu_soc *ipu);
|
||||
int ipu_dp_setup_channel(struct ipu_dp *dp,
|
||||
enum ipu_color_space in, enum ipu_color_space out);
|
||||
|
@ -308,6 +330,21 @@ int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
|
|||
int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
|
||||
bool bg_chan);
|
||||
|
||||
/*
|
||||
* IPU Prefetch Resolve Gasket (prg) functions
|
||||
*/
|
||||
int ipu_prg_max_active_channels(void);
|
||||
bool ipu_prg_present(struct ipu_soc *ipu);
|
||||
bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
|
||||
uint64_t modifier);
|
||||
int ipu_prg_enable(struct ipu_soc *ipu);
|
||||
void ipu_prg_disable(struct ipu_soc *ipu);
|
||||
void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan);
|
||||
int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
|
||||
unsigned int axi_id, unsigned int width,
|
||||
unsigned int height, unsigned int stride,
|
||||
u32 format, unsigned long *eba);
|
||||
|
||||
/*
|
||||
* IPU CMOS Sensor Interface (csi) functions
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue