diff options
Diffstat (limited to 'drivers/staging/imx-drm/ipuv3-crtc.c')
| -rw-r--r-- | drivers/staging/imx-drm/ipuv3-crtc.c | 117 |
1 files changed, 89 insertions, 28 deletions
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 22be104fbda..720868bff35 100644 --- a/drivers/staging/imx-drm/ipuv3-crtc.c +++ b/drivers/staging/imx-drm/ipuv3-crtc.c @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ +#include <linux/component.h> #include <linux/module.h> #include <linux/export.h> #include <linux/device.h> @@ -29,7 +30,7 @@ #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> -#include "ipu-v3/imx-ipu-v3.h" +#include <video/imx-ipu-v3.h> #include "imx-drm.h" #include "ipuv3-plane.h" @@ -59,24 +60,32 @@ struct ipu_crtc { static void ipu_fb_enable(struct ipu_crtc *ipu_crtc) { + struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + if (ipu_crtc->enabled) return; - ipu_di_enable(ipu_crtc->di); - ipu_dc_enable_channel(ipu_crtc->dc); + ipu_dc_enable(ipu); ipu_plane_enable(ipu_crtc->plane[0]); + /* Start DC channel and DI after IDMAC */ + ipu_dc_enable_channel(ipu_crtc->dc); + ipu_di_enable(ipu_crtc->di); ipu_crtc->enabled = 1; } static void ipu_fb_disable(struct ipu_crtc *ipu_crtc) { + struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); + if (!ipu_crtc->enabled) return; - ipu_plane_disable(ipu_crtc->plane[0]); + /* Stop DC channel and DI before IDMAC */ ipu_dc_disable_channel(ipu_crtc->dc); ipu_di_disable(ipu_crtc->di); + ipu_plane_disable(ipu_crtc->plane[0]); + ipu_dc_disable(ipu); ipu_crtc->enabled = 0; } @@ -120,7 +129,7 @@ static int ipu_page_flip(struct drm_crtc *crtc, ipu_crtc->newfb = fb; ipu_crtc->page_flip_event = event; - crtc->fb = fb; + crtc->primary->fb = fb; return 0; } @@ -157,7 +166,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, sig_cfg.Vsync_pol = 1; sig_cfg.enable_pol = 1; - sig_cfg.clk_pol = 1; + sig_cfg.clk_pol = 0; sig_cfg.width = mode->hdisplay; sig_cfg.height = mode->vdisplay; sig_cfg.pixel_fmt = out_pixel_fmt; @@ -192,7 +201,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, return ret; } - return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->fb, + return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode, crtc->primary->fb, 0, 0, mode->hdisplay, mode->vdisplay, x, y, mode->hdisplay, mode->vdisplay); } @@ -218,7 +227,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id) if (ipu_crtc->newfb) { ipu_crtc->newfb = NULL; - ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.fb, + ipu_plane_set_base(ipu_crtc->plane[0], ipu_crtc->base.primary->fb, ipu_crtc->plane[0]->x, ipu_crtc->plane[0]->y); ipu_crtc_handle_pageflip(ipu_crtc); } @@ -284,6 +293,7 @@ static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT; break; + case DRM_MODE_ENCODER_TMDS: case DRM_MODE_ENCODER_NONE: ipu_crtc->di_clkflags = 0; break; @@ -334,7 +344,7 @@ err_out: } static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, - struct ipu_client_platformdata *pdata) + struct ipu_client_platformdata *pdata, struct drm_device *drm) { struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent); int dp = -EINVAL; @@ -348,10 +358,8 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, return ret; } - ret = imx_drm_add_crtc(&ipu_crtc->base, - &ipu_crtc->imx_crtc, - &ipu_crtc_helper_funcs, THIS_MODULE, - ipu_crtc->dev->parent->of_node, pdata->di); + ret = imx_drm_add_crtc(drm, &ipu_crtc->base, &ipu_crtc->imx_crtc, + &ipu_crtc_helper_funcs, ipu_crtc->dev->of_node); if (ret) { dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); goto err_put_resources; @@ -399,43 +407,96 @@ err_put_resources: return ret; } -static int ipu_drm_probe(struct platform_device *pdev) +static struct device_node *ipu_drm_get_port_by_id(struct device_node *parent, + int port_id) { - struct ipu_client_platformdata *pdata = pdev->dev.platform_data; - struct ipu_crtc *ipu_crtc; - int ret; + struct device_node *port; + int id, ret; + + port = of_get_child_by_name(parent, "port"); + while (port) { + ret = of_property_read_u32(port, "reg", &id); + if (!ret && id == port_id) + return port; + + do { + port = of_get_next_child(parent, port); + if (!port) + return NULL; + } while (of_node_cmp(port->name, "port")); + } - if (!pdata) - return -EINVAL; + return NULL; +} - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; +static int ipu_drm_bind(struct device *dev, struct device *master, void *data) +{ + struct ipu_client_platformdata *pdata = dev->platform_data; + struct drm_device *drm = data; + struct ipu_crtc *ipu_crtc; + int ret; - ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL); + ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL); if (!ipu_crtc) return -ENOMEM; - ipu_crtc->dev = &pdev->dev; + ipu_crtc->dev = dev; - ret = ipu_crtc_init(ipu_crtc, pdata); + ret = ipu_crtc_init(ipu_crtc, pdata, drm); if (ret) return ret; - platform_set_drvdata(pdev, ipu_crtc); + dev_set_drvdata(dev, ipu_crtc); return 0; } -static int ipu_drm_remove(struct platform_device *pdev) +static void ipu_drm_unbind(struct device *dev, struct device *master, + void *data) { - struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev); + struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev); imx_drm_remove_crtc(ipu_crtc->imx_crtc); ipu_plane_put_resources(ipu_crtc->plane[0]); ipu_put_resources(ipu_crtc); +} + +static const struct component_ops ipu_crtc_ops = { + .bind = ipu_drm_bind, + .unbind = ipu_drm_unbind, +}; + +static int ipu_drm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ipu_client_platformdata *pdata = dev->platform_data; + int ret; + + if (!dev->platform_data) + return -EINVAL; + + if (!dev->of_node) { + /* Associate crtc device with the corresponding DI port node */ + dev->of_node = ipu_drm_get_port_by_id(dev->parent->of_node, + pdata->di + 2); + if (!dev->of_node) { + dev_err(dev, "missing port@%d node in %s\n", + pdata->di + 2, dev->parent->of_node->full_name); + return -ENODEV; + } + } + + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return component_add(dev, &ipu_crtc_ops); +} +static int ipu_drm_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &ipu_crtc_ops); return 0; } |
