Commit 58e75731 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge branch 'imx-drm-staging' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into staging-next

Russell writes:

These changes, which convert imx-drm to use the recently merged
component infrastructure, have been reviewed and acked by Philipp Zabel,
Shawn Guo and Fabio Estevam, and are now deemed to be ready.
parents b4206006 d94905e0
...@@ -21,7 +21,7 @@ memory { ...@@ -21,7 +21,7 @@ memory {
reg = <0x90000000 0x20000000>; reg = <0x90000000 0x20000000>;
}; };
display@di0 { display0: display@di0 {
compatible = "fsl,imx-parallel-display"; compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>; crtcs = <&ipu 0>;
interface-pix-fmt = "rgb24"; interface-pix-fmt = "rgb24";
...@@ -43,7 +43,7 @@ timing0: dvi { ...@@ -43,7 +43,7 @@ timing0: dvi {
}; };
}; };
display@di1 { display1: display@di1 {
compatible = "fsl,imx-parallel-display"; compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>; crtcs = <&ipu 1>;
interface-pix-fmt = "rgb565"; interface-pix-fmt = "rgb565";
...@@ -81,6 +81,12 @@ power { ...@@ -81,6 +81,12 @@ power {
}; };
}; };
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>, <&ipu 1>;
connectors = <&display0>, <&display1>;
};
sound { sound {
compatible = "fsl,imx51-babbage-sgtl5000", compatible = "fsl,imx51-babbage-sgtl5000",
"fsl,imx-audio-sgtl5000"; "fsl,imx-audio-sgtl5000";
......
...@@ -21,7 +21,7 @@ memory { ...@@ -21,7 +21,7 @@ memory {
}; };
soc { soc {
display@di1 { display1: display@di1 {
compatible = "fsl,imx-parallel-display"; compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>; crtcs = <&ipu 1>;
interface-pix-fmt = "bgr666"; interface-pix-fmt = "bgr666";
...@@ -53,6 +53,12 @@ backlight { ...@@ -53,6 +53,12 @@ backlight {
default-brightness-level = <6>; default-brightness-level = <6>;
}; };
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&display1>;
};
leds { leds {
compatible = "gpio-leds"; compatible = "gpio-leds";
pinctrl-names = "default"; pinctrl-names = "default";
......
...@@ -43,6 +43,12 @@ disp1: display@disp1 { ...@@ -43,6 +43,12 @@ disp1: display@disp1 {
status = "disabled"; status = "disabled";
}; };
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&disp1>, <&tve>;
};
reg_3p2v: 3p2v { reg_3p2v: 3p2v {
compatible = "regulator-fixed"; compatible = "regulator-fixed";
regulator-name = "3P2V"; regulator-name = "3P2V";
......
...@@ -21,7 +21,7 @@ memory { ...@@ -21,7 +21,7 @@ memory {
reg = <0x70000000 0x40000000>; reg = <0x70000000 0x40000000>;
}; };
display@di0 { display0: display@di0 {
compatible = "fsl,imx-parallel-display"; compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>; crtcs = <&ipu 0>;
interface-pix-fmt = "rgb565"; interface-pix-fmt = "rgb565";
...@@ -72,6 +72,12 @@ volume-down { ...@@ -72,6 +72,12 @@ volume-down {
}; };
}; };
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>;
connectors = <&display0>;
};
leds { leds {
compatible = "gpio-leds"; compatible = "gpio-leds";
pinctrl-names = "default"; pinctrl-names = "default";
......
...@@ -88,3 +88,8 @@ lvds-channel@1 { ...@@ -88,3 +88,8 @@ lvds-channel@1 {
crtcs = <&ipu1 0>, <&ipu1 1>; crtcs = <&ipu1 0>, <&ipu1 1>;
}; };
}; };
&hdmi {
compatible = "fsl,imx6dl-hdmi";
crtcs = <&ipu1 0>, <&ipu1 1>;
};
...@@ -20,6 +20,10 @@ / { ...@@ -20,6 +20,10 @@ / {
compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
}; };
&imx_drm {
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
};
&sata { &sata {
status = "okay"; status = "okay";
}; };
...@@ -159,3 +159,8 @@ lvds-channel@1 { ...@@ -159,3 +159,8 @@ lvds-channel@1 {
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
}; };
}; };
&hdmi {
compatible = "fsl,imx6q-hdmi";
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
};
...@@ -62,6 +62,12 @@ volume-down { ...@@ -62,6 +62,12 @@ volume-down {
}; };
}; };
imx_drm: imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu1 0>, <&ipu1 1>;
connectors = <&ldb>;
};
sound { sound {
compatible = "fsl,imx6q-sabresd-wm8962", compatible = "fsl,imx6q-sabresd-wm8962",
"fsl,imx-audio-wm8962"; "fsl,imx-audio-wm8962";
......
...@@ -1368,6 +1368,15 @@ lvds-channel@1 { ...@@ -1368,6 +1368,15 @@ lvds-channel@1 {
}; };
}; };
hdmi: hdmi@0120000 {
reg = <0x00120000 0x9000>;
interrupts = <0 115 0x04>;
gpr = <&gpr>;
clocks = <&clks 123>, <&clks 124>;
clock-names = "iahb", "isfr";
status = "disabled";
};
dcic1: dcic@020e4000 { dcic1: dcic@020e4000 {
reg = <0x020e4000 0x4000>; reg = <0x020e4000 0x4000>;
interrupts = <0 124 0x04>; interrupts = <0 124 0x04>;
......
imxdrm-objs := imx-drm-core.o imx-fb.o imxdrm-objs := imx-drm-core.o
obj-$(CONFIG_DRM_IMX) += imxdrm.o obj-$(CONFIG_DRM_IMX) += imxdrm.o
obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o
obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/ obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o
......
This diff is collapsed.
...@@ -5,13 +5,15 @@ ...@@ -5,13 +5,15 @@
#define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') #define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3')
struct device_node;
struct drm_crtc; struct drm_crtc;
struct drm_connector; struct drm_connector;
struct drm_device; struct drm_device;
struct drm_display_mode;
struct drm_encoder; struct drm_encoder;
struct imx_drm_crtc;
struct drm_fbdev_cma; struct drm_fbdev_cma;
struct drm_framebuffer; struct drm_framebuffer;
struct imx_drm_crtc;
struct platform_device; struct platform_device;
int imx_drm_crtc_id(struct imx_drm_crtc *crtc); int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
...@@ -25,10 +27,10 @@ struct imx_drm_crtc_helper_funcs { ...@@ -25,10 +27,10 @@ struct imx_drm_crtc_helper_funcs {
const struct drm_crtc_funcs *crtc_funcs; const struct drm_crtc_funcs *crtc_funcs;
}; };
int imx_drm_add_crtc(struct drm_crtc *crtc, int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
struct imx_drm_crtc **new_crtc, struct imx_drm_crtc **new_crtc,
const struct imx_drm_crtc_helper_funcs *imx_helper_funcs, const struct imx_drm_crtc_helper_funcs *imx_helper_funcs,
struct module *owner, void *cookie, int id); void *cookie, int id);
int imx_drm_remove_crtc(struct imx_drm_crtc *); int imx_drm_remove_crtc(struct imx_drm_crtc *);
int imx_drm_init_drm(struct platform_device *pdev, int imx_drm_init_drm(struct platform_device *pdev,
int preferred_bpp); int preferred_bpp);
...@@ -38,35 +40,22 @@ int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc); ...@@ -38,35 +40,22 @@ int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc);
void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc); void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc);
void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc); void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc);
struct imx_drm_encoder;
int imx_drm_add_encoder(struct drm_encoder *encoder,
struct imx_drm_encoder **new_enc,
struct module *owner);
int imx_drm_remove_encoder(struct imx_drm_encoder *);
struct imx_drm_connector;
int imx_drm_add_connector(struct drm_connector *connector,
struct imx_drm_connector **new_con,
struct module *owner);
int imx_drm_remove_connector(struct imx_drm_connector *);
void imx_drm_mode_config_init(struct drm_device *drm); void imx_drm_mode_config_init(struct drm_device *drm);
struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
struct drm_device *imx_drm_device_get(void); int imx_drm_panel_format_pins(struct drm_encoder *encoder,
void imx_drm_device_put(void);
int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type,
u32 interface_pix_fmt, int hsync_pin, int vsync_pin); u32 interface_pix_fmt, int hsync_pin, int vsync_pin);
int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type, int imx_drm_panel_format(struct drm_encoder *encoder,
u32 interface_pix_fmt); u32 interface_pix_fmt);
void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper);
struct device_node; int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder);
int imx_drm_encoder_parse_of(struct drm_device *drm,
struct drm_encoder *encoder, struct device_node *np);
int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, int imx_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_crtc *crtc); struct drm_display_mode *mode);
int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, void imx_drm_connector_destroy(struct drm_connector *connector);
struct device_node *np); void imx_drm_encoder_destroy(struct drm_encoder *encoder);
#endif /* _IMX_DRM_H_ */ #endif /* _IMX_DRM_H_ */
/*
* i.MX drm driver
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
*
* Based on Samsung Exynos code
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that 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 <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include "imx-drm.h"
static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
};
void imx_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 64;
dev->mode_config.min_height = 64;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &imx_drm_mode_config_funcs;
}
/*
* i.MX drm driver
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
*
* Based on Samsung Exynos code
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that 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 <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include "imx-drm.h"
#define MAX_CONNECTOR 4
#define PREFERRED_BPP 16
static struct drm_fbdev_cma *fbdev_cma;
static int legacyfb_depth = 16;
module_param(legacyfb_depth, int, 0444);
static int __init imx_fb_helper_init(void)
{
struct drm_device *drm = imx_drm_device_get();
if (!drm)
return -EINVAL;
if (legacyfb_depth != 16 && legacyfb_depth != 32) {
pr_warn("i.MX legacyfb: invalid legacyfb_depth setting. defaulting to 16bpp\n");
legacyfb_depth = 16;
}
fbdev_cma = drm_fbdev_cma_init(drm, legacyfb_depth,
drm->mode_config.num_crtc, MAX_CONNECTOR);
if (IS_ERR(fbdev_cma)) {
imx_drm_device_put();
return PTR_ERR(fbdev_cma);
}
imx_drm_fb_helper_set(fbdev_cma);
return 0;
}
static void __exit imx_fb_helper_exit(void)
{
imx_drm_fb_helper_set(NULL);
drm_fbdev_cma_fini(fbdev_cma);
imx_drm_device_put();
}
late_initcall(imx_fb_helper_init);
module_exit(imx_fb_helper_exit);
MODULE_DESCRIPTION("Freescale i.MX legacy fb driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
This diff is collapsed.
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_fb_helper.h> #include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
...@@ -58,9 +59,8 @@ struct imx_ldb; ...@@ -58,9 +59,8 @@ struct imx_ldb;
struct imx_ldb_channel { struct imx_ldb_channel {
struct imx_ldb *ldb; struct imx_ldb *ldb;
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_connector *imx_drm_connector;
struct drm_encoder encoder; struct drm_encoder encoder;
struct imx_drm_encoder *imx_drm_encoder; struct device_node *child;
int chno; int chno;
void *edid; void *edid;
int edid_len; int edid_len;
...@@ -91,11 +91,6 @@ static enum drm_connector_status imx_ldb_connector_detect( ...@@ -91,11 +91,6 @@ static enum drm_connector_status imx_ldb_connector_detect(
return connector_status_connected; return connector_status_connected;
} }
static void imx_ldb_connector_destroy(struct drm_connector *connector)
{
/* do not free here */
}
static int imx_ldb_connector_get_modes(struct drm_connector *connector) static int imx_ldb_connector_get_modes(struct drm_connector *connector)
{ {
struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
...@@ -120,12 +115,6 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) ...@@ -120,12 +115,6 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)
return num_modes; return num_modes;
} }
static int imx_ldb_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return 0;
}
static struct drm_encoder *imx_ldb_connector_best_encoder( static struct drm_encoder *imx_ldb_connector_best_encoder(
struct drm_connector *connector) struct drm_connector *connector)
{ {
...@@ -179,8 +168,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) ...@@ -179,8 +168,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
u32 pixel_fmt; u32 pixel_fmt;
unsigned long serial_clk; unsigned long serial_clk;
unsigned long di_clk = mode->clock * 1000; unsigned long di_clk = mode->clock * 1000;
int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder, int mux = imx_drm_encoder_get_mux_id(encoder);
encoder->crtc);
if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
/* dual channel LVDS mode */ /* dual channel LVDS mode */
...@@ -207,8 +195,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) ...@@ -207,8 +195,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
pixel_fmt = V4L2_PIX_FMT_RGB24; pixel_fmt = V4L2_PIX_FMT_RGB24;
} }
imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS, imx_drm_panel_format(encoder, pixel_fmt);
pixel_fmt);
} }
static void imx_ldb_encoder_commit(struct drm_encoder *encoder) static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
...@@ -216,8 +203,7 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder) ...@@ -216,8 +203,7 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder, int mux = imx_drm_encoder_get_mux_id(encoder);
encoder->crtc);
if (dual) { if (dual) {
clk_prepare_enable(ldb->clk[0]); clk_prepare_enable(ldb->clk[0]);
...@@ -316,26 +302,21 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder) ...@@ -316,26 +302,21 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
} }
} }
static void imx_ldb_encoder_destroy(struct drm_encoder *encoder)
{
/* do not free here */
}
static struct drm_connector_funcs imx_ldb_connector_funcs = { static struct drm_connector_funcs imx_ldb_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.detect = imx_ldb_connector_detect, .detect = imx_ldb_connector_detect,
.destroy = imx_ldb_connector_destroy, .destroy = imx_drm_connector_destroy,
}; };
static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = { static struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
.get_modes = imx_ldb_connector_get_modes, .get_modes = imx_ldb_connector_get_modes,
.best_encoder = imx_ldb_connector_best_encoder, .best_encoder = imx_ldb_connector_best_encoder,
.mode_valid = imx_ldb_connector_mode_valid, .mode_valid = imx_drm_connector_mode_valid,
}; };
static struct drm_encoder_funcs imx_ldb_encoder_funcs = { static struct drm_encoder_funcs imx_ldb_encoder_funcs = {
.destroy = imx_ldb_encoder_destroy, .destroy = imx_drm_encoder_destroy,
}; };
static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { static struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
...@@ -362,45 +343,36 @@ static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) ...@@ -362,45 +343,36 @@ static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]); return PTR_ERR_OR_ZERO(ldb->clk_pll[chno]);
} }
static int imx_ldb_register(struct imx_ldb_channel *imx_ldb_ch) static int imx_ldb_register(struct drm_device *drm,
struct imx_ldb_channel *imx_ldb_ch)
{ {
int ret;
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int ret;
ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder,
imx_ldb_ch->child);
if (ret)
return ret;
ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno); ret = imx_ldb_get_clk(ldb, imx_ldb_ch->chno);
if (ret) if (ret)
return ret; return ret;
if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
ret |= imx_ldb_get_clk(ldb, 1); ret = imx_ldb_get_clk(ldb, 1);
if (ret) if (ret)
return ret; return ret;
} }
imx_ldb_ch->connector.funcs = &imx_ldb_connector_funcs;
imx_ldb_ch->encoder.funcs = &imx_ldb_encoder_funcs;
imx_ldb_ch->encoder.encoder_type = DRM_MODE_ENCODER_LVDS;
imx_ldb_ch->connector.connector_type = DRM_MODE_CONNECTOR_LVDS;
drm_encoder_helper_add(&imx_ldb_ch->encoder, drm_encoder_helper_add(&imx_ldb_ch->encoder,
&imx_ldb_encoder_helper_funcs); &imx_ldb_encoder_helper_funcs);
ret = imx_drm_add_encoder(&imx_ldb_ch->encoder, drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs,
&imx_ldb_ch->imx_drm_encoder, THIS_MODULE); DRM_MODE_ENCODER_LVDS);
if (ret) {
dev_err(ldb->dev, "adding encoder failed with %d\n", ret);
return ret;
}
drm_connector_helper_add(&imx_ldb_ch->connector, drm_connector_helper_add(&imx_ldb_ch->connector,
&imx_ldb_connector_helper_funcs); &imx_ldb_connector_helper_funcs);
drm_connector_init(drm, &imx_ldb_ch->connector,
ret = imx_drm_add_connector(&imx_ldb_ch->connector, &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
&imx_ldb_ch->imx_drm_connector, THIS_MODULE);
if (ret) {
imx_drm_remove_encoder(imx_ldb_ch->imx_drm_encoder);
dev_err(ldb->dev, "adding connector failed with %d\n", ret);
return ret;
}
drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
&imx_ldb_ch->encoder); &imx_ldb_ch->encoder);
...@@ -459,11 +431,12 @@ static const struct of_device_id imx_ldb_dt_ids[] = { ...@@ -459,11 +431,12 @@ static const struct of_device_id imx_ldb_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids); MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
static int imx_ldb_probe(struct platform_device *pdev) static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
{ {
struct device_node *np = pdev->dev.of_node; struct drm_device *drm = data;
struct device_node *np = dev->of_node;
const struct of_device_id *of_id = const struct of_device_id *of_id =
of_match_device(imx_ldb_dt_ids, &pdev->dev); of_match_device(imx_ldb_dt_ids, dev);
struct device_node *child; struct device_node *child;
const u8 *edidp; const u8 *edidp;
struct imx_ldb *imx_ldb; struct imx_ldb *imx_ldb;
...@@ -473,17 +446,17 @@ static int imx_ldb_probe(struct platform_device *pdev) ...@@ -473,17 +446,17 @@ static int imx_ldb_probe(struct platform_device *pdev)
int ret; int ret;
int i; int i;
imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL); imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL);
if (!imx_ldb) if (!imx_ldb)
return -ENOMEM; return -ENOMEM;
imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
if (IS_ERR(imx_ldb->regmap)) { if (IS_ERR(imx_ldb->regmap)) {
dev_err(&pdev->dev, "failed to get parent regmap\n"); dev_err(dev, "failed to get parent regmap\n");
return PTR_ERR(imx_ldb->regmap); return PTR_ERR(imx_ldb->regmap);
} }
imx_ldb->dev = &pdev->dev; imx_ldb->dev = dev;
if (of_id) if (of_id)
imx_ldb->lvds_mux = of_id->data; imx_ldb->lvds_mux = of_id->data;
...@@ -521,7 +494,7 @@ static int imx_ldb_probe(struct platform_device *pdev) ...@@ -521,7 +494,7 @@ static int imx_ldb_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
if (dual && i > 0) { if (dual && i > 0) {
dev_warn(&pdev->dev, "dual-channel mode, ignoring second output\n"); dev_warn(dev, "dual-channel mode, ignoring second output\n");
continue; continue;
} }
...@@ -531,6 +504,7 @@ static int imx_ldb_probe(struct platform_device *pdev) ...@@ -531,6 +504,7 @@ static int imx_ldb_probe(struct platform_device *pdev)
channel = &imx_ldb->channel[i]; channel = &imx_ldb->channel[i];
channel->ldb = imx_ldb; channel->ldb = imx_ldb;
channel->chno = i; channel->chno = i;
channel->child = child;
edidp = of_get_property(child, "edid", &channel->edid_len); edidp = of_get_property(child, "edid", &channel->edid_len);
if (edidp) { if (edidp) {
...@@ -560,7 +534,7 @@ static int imx_ldb_probe(struct platform_device *pdev) ...@@ -560,7 +534,7 @@ static int imx_ldb_probe(struct platform_device *pdev)
break; break;
case LVDS_BIT_MAP_JEIDA: case LVDS_BIT_MAP_JEIDA:
if (datawidth == 18) { if (datawidth == 18) {
dev_err(&pdev->dev, "JEIDA standard only supported in 24 bit\n"); dev_err(dev, "JEIDA standard only supported in 24 bit\n");
return -EINVAL; return -EINVAL;
} }
if (i == 0 || dual) if (i == 0 || dual)
...@@ -569,38 +543,47 @@ static int imx_ldb_probe(struct platform_device *pdev) ...@@ -569,38 +543,47 @@ static int imx_ldb_probe(struct platform_device *pdev)
imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA; imx_ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | LDB_BIT_MAP_CH1_JEIDA;
break; break;
default: default:
dev_err(&pdev->dev, "data mapping not specified or invalid\n"); dev_err(dev, "data mapping not specified or invalid\n");
return -EINVAL; return -EINVAL;
} }
ret = imx_ldb_register(channel); ret = imx_ldb_register(drm, channel);
if (ret) if (ret)
return ret; return ret;
imx_drm_encoder_add_possible_crtcs(channel->imx_drm_encoder, child);
} }
platform_set_drvdata(pdev, imx_ldb); dev_set_drvdata(dev, imx_ldb);
return 0; return 0;
} }
static int imx_ldb_remove(struct platform_device *pdev) static void imx_ldb_unbind(struct device *dev, struct device *master,
void *data)
{ {
struct imx_ldb *imx_ldb = platform_get_drvdata(pdev); struct imx_ldb *imx_ldb = dev_get_drvdata(dev);
int i; int i;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
struct imx_ldb_channel *channel = &imx_ldb->channel[i]; struct imx_ldb_channel *channel = &imx_ldb->channel[i];
struct drm_connector *connector = &channel->connector;
struct drm_encoder *encoder = &channel->encoder;
drm_mode_connector_detach_encoder(connector, encoder);
imx_drm_remove_connector(channel->imx_drm_connector); channel->connector.funcs->destroy(&channel->connector);
imx_drm_remove_encoder(channel->imx_drm_encoder); channel->encoder.funcs->destroy(&channel->encoder);
} }
}
static const struct component_ops imx_ldb_ops = {
.bind = imx_ldb_bind,
.unbind = imx_ldb_unbind,
};
static int imx_ldb_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &imx_ldb_ops);
}
static int imx_ldb_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &imx_ldb_ops);
return 0; return 0;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/component.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -110,9 +111,7 @@ enum { ...@@ -110,9 +111,7 @@ enum {
struct imx_tve { struct imx_tve {
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_connector *imx_drm_connector;
struct drm_encoder encoder; struct drm_encoder encoder;
struct imx_drm_encoder *imx_drm_encoder;
struct device *dev; struct device *dev;
spinlock_t lock; /* register lock */ spinlock_t lock; /* register lock */
bool enabled; bool enabled;
...@@ -225,11 +224,6 @@ static enum drm_connector_status imx_tve_connector_detect( ...@@ -225,11 +224,6 @@ static enum drm_connector_status imx_tve_connector_detect(
return connector_status_connected; return connector_status_connected;
} }
static void imx_tve_connector_destroy(struct drm_connector *connector)
{
/* do not free here */
}
static int imx_tve_connector_get_modes(struct drm_connector *connector) static int imx_tve_connector_get_modes(struct drm_connector *connector)
{ {
struct imx_tve *tve = con_to_tve(connector); struct imx_tve *tve = con_to_tve(connector);
...@@ -254,6 +248,11 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, ...@@ -254,6 +248,11 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector,
{ {
struct imx_tve *tve = con_to_tve(connector); struct imx_tve *tve = con_to_tve(connector);
unsigned long rate; unsigned long rate;
int ret;
ret = imx_drm_connector_mode_valid(connector, mode);
if (ret != MODE_OK)
return ret;
/* pixel clock with 2x oversampling */ /* pixel clock with 2x oversampling */
rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000;
...@@ -305,13 +304,11 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) ...@@ -305,13 +304,11 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder)
switch (tve->mode) { switch (tve->mode) {
case TVE_MODE_VGA: case TVE_MODE_VGA:
imx_drm_crtc_panel_format_pins(encoder->crtc, imx_drm_panel_format_pins(encoder, IPU_PIX_FMT_GBR24,
DRM_MODE_ENCODER_DAC, IPU_PIX_FMT_GBR24,
tve->hsync_pin, tve->vsync_pin); tve->hsync_pin, tve->vsync_pin);
break; break;
case TVE_MODE_TVOUT: case TVE_MODE_TVOUT:
imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_TVDAC, imx_drm_panel_format(encoder, V4L2_PIX_FMT_YUV444);
V4L2_PIX_FMT_YUV444);
break; break;
} }
} }
...@@ -364,16 +361,11 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder) ...@@ -364,16 +361,11 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder)
tve_disable(tve); tve_disable(tve);
} }
static void imx_tve_encoder_destroy(struct drm_encoder *encoder)
{
/* do not free here */
}
static struct drm_connector_funcs imx_tve_connector_funcs = { static struct drm_connector_funcs imx_tve_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.detect = imx_tve_connector_detect, .detect = imx_tve_connector_detect,
.destroy = imx_tve_connector_destroy, .destroy = imx_drm_connector_destroy,
}; };
static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = {
...@@ -383,7 +375,7 @@ static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = { ...@@ -383,7 +375,7 @@ static struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = {
}; };
static struct drm_encoder_funcs imx_tve_encoder_funcs = { static struct drm_encoder_funcs imx_tve_encoder_funcs = {
.destroy = imx_tve_encoder_destroy, .destroy = imx_drm_encoder_destroy,
}; };
static struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = { static struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = {
...@@ -503,34 +495,27 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base) ...@@ -503,34 +495,27 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base)
return 0; return 0;
} }
static int imx_tve_register(struct imx_tve *tve) static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
{ {
int encoder_type;
int ret; int ret;
tve->connector.funcs = &imx_tve_connector_funcs; encoder_type = tve->mode == TVE_MODE_VGA ?
tve->encoder.funcs = &imx_tve_encoder_funcs; DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC;
tve->encoder.encoder_type = DRM_MODE_ENCODER_NONE; ret = imx_drm_encoder_parse_of(drm, &tve->encoder,
tve->connector.connector_type = DRM_MODE_CONNECTOR_VGA; tve->dev->of_node);
if (ret)
return ret;
drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs);
ret = imx_drm_add_encoder(&tve->encoder, &tve->imx_drm_encoder, drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs,
THIS_MODULE); encoder_type);
if (ret) {
dev_err(tve->dev, "adding encoder failed with %d\n", ret);
return ret;
}
drm_connector_helper_add(&tve->connector, drm_connector_helper_add(&tve->connector,
&imx_tve_connector_helper_funcs); &imx_tve_connector_helper_funcs);
drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs,
ret = imx_drm_add_connector(&tve->connector, DRM_MODE_CONNECTOR_VGA);
&tve->imx_drm_connector, THIS_MODULE);
if (ret) {
imx_drm_remove_encoder(tve->imx_drm_encoder);
dev_err(tve->dev, "adding connector failed with %d\n", ret);
return ret;
}
drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder);
...@@ -576,9 +561,11 @@ static const int of_get_tve_mode(struct device_node *np) ...@@ -576,9 +561,11 @@ static const int of_get_tve_mode(struct device_node *np)
return -EINVAL; return -EINVAL;
} }
static int imx_tve_probe(struct platform_device *pdev) static int imx_tve_bind(struct device *dev, struct device *master, void *data)
{ {
struct device_node *np = pdev->dev.of_node; struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
struct device_node *np = dev->of_node;
struct device_node *ddc_node; struct device_node *ddc_node;
struct imx_tve *tve; struct imx_tve *tve;
struct resource *res; struct resource *res;
...@@ -587,11 +574,11 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -587,11 +574,11 @@ static int imx_tve_probe(struct platform_device *pdev)
int irq; int irq;
int ret; int ret;
tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL); tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL);
if (!tve) if (!tve)
return -ENOMEM; return -ENOMEM;
tve->dev = &pdev->dev; tve->dev = dev;
spin_lock_init(&tve->lock); spin_lock_init(&tve->lock);
ddc_node = of_parse_phandle(np, "ddc", 0); ddc_node = of_parse_phandle(np, "ddc", 0);
...@@ -602,7 +589,7 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -602,7 +589,7 @@ static int imx_tve_probe(struct platform_device *pdev)
tve->mode = of_get_tve_mode(np); tve->mode = of_get_tve_mode(np);
if (tve->mode != TVE_MODE_VGA) { if (tve->mode != TVE_MODE_VGA) {
dev_err(&pdev->dev, "only VGA mode supported, currently\n"); dev_err(dev, "only VGA mode supported, currently\n");
return -EINVAL; return -EINVAL;
} }
...@@ -611,7 +598,7 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -611,7 +598,7 @@ static int imx_tve_probe(struct platform_device *pdev)
&tve->hsync_pin); &tve->hsync_pin);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to get vsync pin\n"); dev_err(dev, "failed to get vsync pin\n");
return ret; return ret;
} }
...@@ -619,40 +606,40 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -619,40 +606,40 @@ static int imx_tve_probe(struct platform_device *pdev)
&tve->vsync_pin); &tve->vsync_pin);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to get vsync pin\n"); dev_err(dev, "failed to get vsync pin\n");
return ret; return ret;
} }
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res); base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
tve_regmap_config.lock_arg = tve; tve_regmap_config.lock_arg = tve;
tve->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "tve", base, tve->regmap = devm_regmap_init_mmio_clk(dev, "tve", base,
&tve_regmap_config); &tve_regmap_config);
if (IS_ERR(tve->regmap)) { if (IS_ERR(tve->regmap)) {
dev_err(&pdev->dev, "failed to init regmap: %ld\n", dev_err(dev, "failed to init regmap: %ld\n",
PTR_ERR(tve->regmap)); PTR_ERR(tve->regmap));
return PTR_ERR(tve->regmap); return PTR_ERR(tve->regmap);
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n"); dev_err(dev, "failed to get irq\n");
return irq; return irq;
} }
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ret = devm_request_threaded_irq(dev, irq, NULL,
imx_tve_irq_handler, IRQF_ONESHOT, imx_tve_irq_handler, IRQF_ONESHOT,
"imx-tve", tve); "imx-tve", tve);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to request irq: %d\n", ret); dev_err(dev, "failed to request irq: %d\n", ret);
return ret; return ret;
} }
tve->dac_reg = devm_regulator_get(&pdev->dev, "dac"); tve->dac_reg = devm_regulator_get(dev, "dac");
if (!IS_ERR(tve->dac_reg)) { if (!IS_ERR(tve->dac_reg)) {
regulator_set_voltage(tve->dac_reg, 2750000, 2750000); regulator_set_voltage(tve->dac_reg, 2750000, 2750000);
ret = regulator_enable(tve->dac_reg); ret = regulator_enable(tve->dac_reg);
...@@ -660,17 +647,17 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -660,17 +647,17 @@ static int imx_tve_probe(struct platform_device *pdev)
return ret; return ret;
} }
tve->clk = devm_clk_get(&pdev->dev, "tve"); tve->clk = devm_clk_get(dev, "tve");
if (IS_ERR(tve->clk)) { if (IS_ERR(tve->clk)) {
dev_err(&pdev->dev, "failed to get high speed tve clock: %ld\n", dev_err(dev, "failed to get high speed tve clock: %ld\n",
PTR_ERR(tve->clk)); PTR_ERR(tve->clk));
return PTR_ERR(tve->clk); return PTR_ERR(tve->clk);
} }
/* this is the IPU DI clock input selector, can be parented to tve_di */ /* this is the IPU DI clock input selector, can be parented to tve_di */
tve->di_sel_clk = devm_clk_get(&pdev->dev, "di_sel"); tve->di_sel_clk = devm_clk_get(dev, "di_sel");
if (IS_ERR(tve->di_sel_clk)) { if (IS_ERR(tve->di_sel_clk)) {
dev_err(&pdev->dev, "failed to get ipu di mux clock: %ld\n", dev_err(dev, "failed to get ipu di mux clock: %ld\n",
PTR_ERR(tve->di_sel_clk)); PTR_ERR(tve->di_sel_clk));
return PTR_ERR(tve->di_sel_clk); return PTR_ERR(tve->di_sel_clk);
} }
...@@ -681,42 +668,51 @@ static int imx_tve_probe(struct platform_device *pdev) ...@@ -681,42 +668,51 @@ static int imx_tve_probe(struct platform_device *pdev)
ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to read configuration register: %d\n", ret); dev_err(dev, "failed to read configuration register: %d\n", ret);
return ret; return ret;
} }
if (val != 0x00100000) { if (val != 0x00100000) {
dev_err(&pdev->dev, "configuration register default value indicates this is not a TVEv2\n"); dev_err(dev, "configuration register default value indicates this is not a TVEv2\n");
return -ENODEV; return -ENODEV;
} }
/* disable cable detection for VGA mode */ /* disable cable detection for VGA mode */
ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0);
ret = imx_tve_register(tve); ret = imx_tve_register(drm, tve);
if (ret) if (ret)
return ret; return ret;
ret = imx_drm_encoder_add_possible_crtcs(tve->imx_drm_encoder, np); dev_set_drvdata(dev, tve);
platform_set_drvdata(pdev, tve);
return 0; return 0;
} }
static int imx_tve_remove(struct platform_device *pdev) static void imx_tve_unbind(struct device *dev, struct device *master,
void *data)
{ {
struct imx_tve *tve = platform_get_drvdata(pdev); struct imx_tve *tve = dev_get_drvdata(dev);
struct drm_connector *connector = &tve->connector;
struct drm_encoder *encoder = &tve->encoder;
drm_mode_connector_detach_encoder(connector, encoder); tve->connector.funcs->destroy(&tve->connector);
tve->encoder.funcs->destroy(&tve->encoder);
imx_drm_remove_connector(tve->imx_drm_connector);
imx_drm_remove_encoder(tve->imx_drm_encoder);
if (!IS_ERR(tve->dac_reg)) if (!IS_ERR(tve->dac_reg))
regulator_disable(tve->dac_reg); regulator_disable(tve->dac_reg);
}
static const struct component_ops imx_tve_ops = {
.bind = imx_tve_bind,
.unbind = imx_tve_unbind,
};
static int imx_tve_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &imx_tve_ops);
}
static int imx_tve_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &imx_tve_ops);
return 0; return 0;
} }
......
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include "imx-ipu-v3.h" #include "imx-ipu-v3.h"
#include "ipu-prv.h" #include "ipu-prv.h"
...@@ -33,10 +30,7 @@ struct ipu_di { ...@@ -33,10 +30,7 @@ struct ipu_di {
struct clk *clk_di; /* display input clock */ struct clk *clk_di; /* display input clock */
struct clk *clk_ipu; /* IPU bus clock */ struct clk *clk_ipu; /* IPU bus clock */
struct clk *clk_di_pixel; /* resulting pixel clock */ struct clk *clk_di_pixel; /* resulting pixel clock */
struct clk_hw clk_hw_out;
char *clk_name;
bool inuse; bool inuse;
unsigned long clkflags;
struct ipu_soc *ipu; struct ipu_soc *ipu;
}; };
...@@ -141,130 +135,6 @@ static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset) ...@@ -141,130 +135,6 @@ static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
writel(value, di->base + offset); writel(value, di->base + offset);
} }
static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
{
u64 tmp = inrate;
int div;
tmp *= 16;
do_div(tmp, outrate);
div = tmp;
if (div < 0x10)
div = 0x10;
#ifdef WTF_IS_THIS
/*
* Freescale has this in their Kernel. It is neither clear what
* it does nor why it does it
*/
if (div & 0x10)
div &= ~0x7;
else {
/* Round up divider if it gets us closer to desired pix clk */
if ((div & 0xC) == 0xC) {
div += 0x10;
div &= ~0xF;
}
}
#endif
return div;
}
static unsigned long clk_di_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
unsigned long outrate;
u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
if (div < 0x10)
div = 0x10;
outrate = (parent_rate / div) * 16;
return outrate;
}
static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
unsigned long outrate;
int div;
u32 val;
div = ipu_di_clk_calc_div(*prate, rate);
outrate = (*prate / div) * 16;
val = ipu_di_read(di, DI_GENERAL);
if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
outrate = *prate / 2;
dev_dbg(di->ipu->dev,
"%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
__func__, *prate, div, outrate, rate);
return outrate;
}
static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
int div;
u32 clkgen0;
clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
div = ipu_di_clk_calc_div(parent_rate, rate);
ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
__func__, parent_rate, rate, div);
return 0;
}
static u8 clk_di_get_parent(struct clk_hw *hw)
{
struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
u32 val;
val = ipu_di_read(di, DI_GENERAL);
return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
}
static int clk_di_set_parent(struct clk_hw *hw, u8 index)
{
struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
u32 val;
val = ipu_di_read(di, DI_GENERAL);
if (index)
val |= DI_GEN_DI_CLK_EXT;
else
val &= ~DI_GEN_DI_CLK_EXT;
ipu_di_write(di, val, DI_GENERAL);
return 0;
}
static struct clk_ops clk_di_ops = {
.round_rate = clk_di_round_rate,
.set_rate = clk_di_set_rate,
.recalc_rate = clk_di_recalc_rate,
.set_parent = clk_di_set_parent,
.get_parent = clk_di_get_parent,
};
static void ipu_di_data_wave_config(struct ipu_di *di, static void ipu_di_data_wave_config(struct ipu_di *di,
int wave_gen, int wave_gen,
int access_size, int component_size) int access_size, int component_size)
...@@ -528,15 +398,125 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di, ...@@ -528,15 +398,125 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga)); ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
} }
static void ipu_di_config_clock(struct ipu_di *di,
const struct ipu_di_signal_cfg *sig)
{
struct clk *clk;
unsigned clkgen0;
uint32_t val;
if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
/*
* CLKMODE_EXT means we must use the DI clock: this is
* needed for things like LVDS which needs to feed the
* DI and LDB with the same pixel clock.
*/
clk = di->clk_di;
if (sig->clkflags & IPU_DI_CLKMODE_SYNC) {
/*
* CLKMODE_SYNC means that we want the DI to be
* clocked at the same rate as the parent clock.
* This is needed (eg) for LDB which needs to be
* fed with the same pixel clock. We assume that
* the LDB clock has already been set correctly.
*/
clkgen0 = 1 << 4;
} else {
/*
* We can use the divider. We should really have
* a flag here indicating whether the bridge can
* cope with a fractional divider or not. For the
* time being, let's go for simplicitly and
* reliability.
*/
unsigned long in_rate;
unsigned div;
clk_set_rate(clk, sig->pixelclock);
in_rate = clk_get_rate(clk);
div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
if (div == 0)
div = 1;
clkgen0 = div << 4;
}
} else {
/*
* For other interfaces, we can arbitarily select between
* the DI specific clock and the internal IPU clock. See
* DI_GENERAL bit 20. We select the IPU clock if it can
* give us a clock rate within 1% of the requested frequency,
* otherwise we use the DI clock.
*/
unsigned long rate, clkrate;
unsigned div, error;
clkrate = clk_get_rate(di->clk_ipu);
div = (clkrate + sig->pixelclock / 2) / sig->pixelclock;
rate = clkrate / div;
error = rate / (sig->pixelclock / 1000);
dev_dbg(di->ipu->dev, " IPU clock can give %lu with divider %u, error %d.%u%%\n",
rate, div, (signed)(error - 1000) / 10, error % 10);
/* Allow a 1% error */
if (error < 1010 && error >= 990) {
clk = di->clk_ipu;
clkgen0 = div << 4;
} else {
unsigned long in_rate;
unsigned div;
clk = di->clk_di;
clk_set_rate(clk, sig->pixelclock);
in_rate = clk_get_rate(clk);
div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
if (div == 0)
div = 1;
clkgen0 = div << 4;
}
}
di->clk_di_pixel = clk;
/* Set the divider */
ipu_di_write(di, clkgen0, DI_BS_CLKGEN0);
/*
* Set the high/low periods. Bits 24:16 give us the falling edge,
* and bits 8:0 give the rising edge. LSB is fraction, and is
* based on the divider above. We want a 50% duty cycle, so set
* the falling edge to be half the divider.
*/
ipu_di_write(di, (clkgen0 >> 4) << 16, DI_BS_CLKGEN1);
/* Finally select the input clock */
val = ipu_di_read(di, DI_GENERAL) & ~DI_GEN_DI_CLK_EXT;
if (clk == di->clk_di)
val |= DI_GEN_DI_CLK_EXT;
ipu_di_write(di, val, DI_GENERAL);
dev_dbg(di->ipu->dev, "Want %luHz IPU %luHz DI %luHz using %s, %luHz\n",
sig->pixelclock,
clk_get_rate(di->clk_ipu),
clk_get_rate(di->clk_di),
clk == di->clk_di ? "DI" : "IPU",
clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
}
int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
{ {
u32 reg; u32 reg;
u32 di_gen, vsync_cnt; u32 di_gen, vsync_cnt;
u32 div; u32 div;
u32 h_total, v_total; u32 h_total, v_total;
int ret;
unsigned long round;
struct clk *parent;
dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n", dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
di->id, sig->width, sig->height); di->id, sig->width, sig->height);
...@@ -544,33 +524,20 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig) ...@@ -544,33 +524,20 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
return -EINVAL; return -EINVAL;
if (sig->clkflags & IPU_DI_CLKMODE_EXT)
parent = di->clk_di;
else
parent = di->clk_ipu;
ret = clk_set_parent(di->clk_di_pixel, parent);
if (ret) {
dev_err(di->ipu->dev,
"setting pixel clock to parent %s failed with %d\n",
__clk_get_name(parent), ret);
return ret;
}
if (sig->clkflags & IPU_DI_CLKMODE_SYNC)
round = clk_get_rate(parent);
else
round = clk_round_rate(di->clk_di_pixel, sig->pixelclock);
ret = clk_set_rate(di->clk_di_pixel, round);
h_total = sig->width + sig->h_sync_width + sig->h_start_width + h_total = sig->width + sig->h_sync_width + sig->h_start_width +
sig->h_end_width; sig->h_end_width;
v_total = sig->height + sig->v_sync_width + sig->v_start_width + v_total = sig->height + sig->v_sync_width + sig->v_start_width +
sig->v_end_width; sig->v_end_width;
dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
clk_get_rate(di->clk_ipu),
clk_get_rate(di->clk_di),
sig->pixelclock);
mutex_lock(&di_mutex); mutex_lock(&di_mutex);
ipu_di_config_clock(di, sig);
div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
div = div / 16; /* Now divider is integer portion */ div = div / 16; /* Now divider is integer portion */
...@@ -654,7 +621,11 @@ EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); ...@@ -654,7 +621,11 @@ EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
int ipu_di_enable(struct ipu_di *di) int ipu_di_enable(struct ipu_di *di)
{ {
int ret = clk_prepare_enable(di->clk_di_pixel); int ret;
WARN_ON(IS_ERR(di->clk_di_pixel));
ret = clk_prepare_enable(di->clk_di_pixel);
if (ret) if (ret)
return ret; return ret;
...@@ -666,6 +637,8 @@ EXPORT_SYMBOL_GPL(ipu_di_enable); ...@@ -666,6 +637,8 @@ EXPORT_SYMBOL_GPL(ipu_di_enable);
int ipu_di_disable(struct ipu_di *di) int ipu_di_disable(struct ipu_di *di)
{ {
WARN_ON(IS_ERR(di->clk_di_pixel));
ipu_module_disable(di->ipu, di->module); ipu_module_disable(di->ipu, di->module);
clk_disable_unprepare(di->clk_di_pixel); clk_disable_unprepare(di->clk_di_pixel);
...@@ -721,13 +694,6 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, ...@@ -721,13 +694,6 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
u32 module, struct clk *clk_ipu) u32 module, struct clk *clk_ipu)
{ {
struct ipu_di *di; struct ipu_di *di;
int ret;
const char *di_parent[2];
struct clk_init_data init = {
.ops = &clk_di_ops,
.num_parents = 2,
.flags = 0,
};
if (id > 1) if (id > 1)
return -ENODEV; return -ENODEV;
...@@ -749,45 +715,16 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, ...@@ -749,45 +715,16 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
if (!di->base) if (!di->base)
return -ENOMEM; return -ENOMEM;
di_parent[0] = __clk_get_name(di->clk_ipu);
di_parent[1] = __clk_get_name(di->clk_di);
ipu_di_write(di, 0x10, DI_BS_CLKGEN0); ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
init.parent_names = (const char **)&di_parent;
di->clk_name = kasprintf(GFP_KERNEL, "%s_di%d_pixel",
dev_name(dev), id);
if (!di->clk_name)
return -ENOMEM;
init.name = di->clk_name;
di->clk_hw_out.init = &init;
di->clk_di_pixel = clk_register(dev, &di->clk_hw_out);
if (IS_ERR(di->clk_di_pixel)) {
ret = PTR_ERR(di->clk_di_pixel);
goto failed_clk_register;
}
dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n", dev_dbg(dev, "DI%d base: 0x%08lx remapped to %p\n",
id, base, di->base); id, base, di->base);
di->inuse = false; di->inuse = false;
di->ipu = ipu; di->ipu = ipu;
return 0; return 0;
failed_clk_register:
kfree(di->clk_name);
return ret;
} }
void ipu_di_exit(struct ipu_soc *ipu, int id) void ipu_di_exit(struct ipu_soc *ipu, int id)
{ {
struct ipu_di *di = ipu->di_priv[id];
clk_unregister(di->clk_di_pixel);
kfree(di->clk_name);
} }
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#include <linux/component.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -284,6 +285,7 @@ static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type, ...@@ -284,6 +285,7 @@ static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC | ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC |
IPU_DI_CLKMODE_EXT; IPU_DI_CLKMODE_EXT;
break; break;
case DRM_MODE_ENCODER_TMDS:
case DRM_MODE_ENCODER_NONE: case DRM_MODE_ENCODER_NONE:
ipu_crtc->di_clkflags = 0; ipu_crtc->di_clkflags = 0;
break; break;
...@@ -334,7 +336,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc, ...@@ -334,7 +336,7 @@ static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
} }
static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, 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); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
int dp = -EINVAL; int dp = -EINVAL;
...@@ -348,9 +350,9 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ...@@ -348,9 +350,9 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
return ret; return ret;
} }
ret = imx_drm_add_crtc(&ipu_crtc->base, ret = imx_drm_add_crtc(drm, &ipu_crtc->base,
&ipu_crtc->imx_crtc, &ipu_crtc->imx_crtc,
&ipu_crtc_helper_funcs, THIS_MODULE, &ipu_crtc_helper_funcs,
ipu_crtc->dev->parent->of_node, pdata->di); ipu_crtc->dev->parent->of_node, pdata->di);
if (ret) { if (ret) {
dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret); dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
...@@ -399,43 +401,61 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, ...@@ -399,43 +401,61 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
return ret; return ret;
} }
static int ipu_drm_probe(struct platform_device *pdev) static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
{ {
struct ipu_client_platformdata *pdata = pdev->dev.platform_data; struct ipu_client_platformdata *pdata = dev->platform_data;
struct drm_device *drm = data;
struct ipu_crtc *ipu_crtc; struct ipu_crtc *ipu_crtc;
int ret; int ret;
if (!pdata) ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL);
return -EINVAL;
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL);
if (!ipu_crtc) if (!ipu_crtc)
return -ENOMEM; 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) if (ret)
return ret; return ret;
platform_set_drvdata(pdev, ipu_crtc); dev_set_drvdata(dev, ipu_crtc);
return 0; 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); imx_drm_remove_crtc(ipu_crtc->imx_crtc);
ipu_plane_put_resources(ipu_crtc->plane[0]); ipu_plane_put_resources(ipu_crtc->plane[0]);
ipu_put_resources(ipu_crtc); 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)
{
int ret;
if (!pdev->dev.platform_data)
return -EINVAL;
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
return component_add(&pdev->dev, &ipu_crtc_ops);
}
static int ipu_drm_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &ipu_crtc_ops);
return 0; return 0;
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* MA 02110-1301, USA. * MA 02110-1301, USA.
*/ */
#include <linux/component.h>
#include <linux/module.h> #include <linux/module.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_fb_helper.h> #include <drm/drm_fb_helper.h>
...@@ -32,9 +33,7 @@ ...@@ -32,9 +33,7 @@
struct imx_parallel_display { struct imx_parallel_display {
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_connector *imx_drm_connector;
struct drm_encoder encoder; struct drm_encoder encoder;
struct imx_drm_encoder *imx_drm_encoder;
struct device *dev; struct device *dev;
void *edid; void *edid;
int edid_len; int edid_len;
...@@ -49,11 +48,6 @@ static enum drm_connector_status imx_pd_connector_detect( ...@@ -49,11 +48,6 @@ static enum drm_connector_status imx_pd_connector_detect(
return connector_status_connected; return connector_status_connected;
} }
static void imx_pd_connector_destroy(struct drm_connector *connector)
{
/* do not free here */
}
static int imx_pd_connector_get_modes(struct drm_connector *connector) static int imx_pd_connector_get_modes(struct drm_connector *connector)
{ {
struct imx_parallel_display *imxpd = con_to_imxpd(connector); struct imx_parallel_display *imxpd = con_to_imxpd(connector);
...@@ -85,12 +79,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) ...@@ -85,12 +79,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
return num_modes; return num_modes;
} }
static int imx_pd_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
return 0;
}
static struct drm_encoder *imx_pd_connector_best_encoder( static struct drm_encoder *imx_pd_connector_best_encoder(
struct drm_connector *connector) struct drm_connector *connector)
{ {
...@@ -114,8 +102,7 @@ static void imx_pd_encoder_prepare(struct drm_encoder *encoder) ...@@ -114,8 +102,7 @@ static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
{ {
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE, imx_drm_panel_format(encoder, imxpd->interface_pix_fmt);
imxpd->interface_pix_fmt);
} }
static void imx_pd_encoder_commit(struct drm_encoder *encoder) static void imx_pd_encoder_commit(struct drm_encoder *encoder)
...@@ -132,26 +119,21 @@ static void imx_pd_encoder_disable(struct drm_encoder *encoder) ...@@ -132,26 +119,21 @@ static void imx_pd_encoder_disable(struct drm_encoder *encoder)
{ {
} }
static void imx_pd_encoder_destroy(struct drm_encoder *encoder)
{
/* do not free here */
}
static struct drm_connector_funcs imx_pd_connector_funcs = { static struct drm_connector_funcs imx_pd_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.detect = imx_pd_connector_detect, .detect = imx_pd_connector_detect,
.destroy = imx_pd_connector_destroy, .destroy = imx_drm_connector_destroy,
}; };
static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = { static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
.get_modes = imx_pd_connector_get_modes, .get_modes = imx_pd_connector_get_modes,
.best_encoder = imx_pd_connector_best_encoder, .best_encoder = imx_pd_connector_best_encoder,
.mode_valid = imx_pd_connector_mode_valid, .mode_valid = imx_drm_connector_mode_valid,
}; };
static struct drm_encoder_funcs imx_pd_encoder_funcs = { static struct drm_encoder_funcs imx_pd_encoder_funcs = {
.destroy = imx_pd_encoder_destroy, .destroy = imx_drm_encoder_destroy,
}; };
static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
...@@ -163,51 +145,42 @@ static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { ...@@ -163,51 +145,42 @@ static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
.disable = imx_pd_encoder_disable, .disable = imx_pd_encoder_disable,
}; };
static int imx_pd_register(struct imx_parallel_display *imxpd) static int imx_pd_register(struct drm_device *drm,
struct imx_parallel_display *imxpd)
{ {
int ret; int ret;
drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder,
imxpd->dev->of_node);
imxpd->connector.funcs = &imx_pd_connector_funcs; if (ret)
imxpd->encoder.funcs = &imx_pd_encoder_funcs; return ret;
imxpd->encoder.encoder_type = DRM_MODE_ENCODER_NONE;
imxpd->connector.connector_type = DRM_MODE_CONNECTOR_VGA;
drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
ret = imx_drm_add_encoder(&imxpd->encoder, &imxpd->imx_drm_encoder, drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs,
THIS_MODULE); DRM_MODE_ENCODER_NONE);
if (ret) {
dev_err(imxpd->dev, "adding encoder failed with %d\n", ret);
return ret;
}
drm_connector_helper_add(&imxpd->connector, drm_connector_helper_add(&imxpd->connector,
&imx_pd_connector_helper_funcs); &imx_pd_connector_helper_funcs);
drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs,
DRM_MODE_CONNECTOR_VGA);
ret = imx_drm_add_connector(&imxpd->connector, drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
&imxpd->imx_drm_connector, THIS_MODULE);
if (ret) {
imx_drm_remove_encoder(imxpd->imx_drm_encoder);
dev_err(imxpd->dev, "adding connector failed with %d\n", ret);
return ret;
}
imxpd->connector.encoder = &imxpd->encoder; imxpd->connector.encoder = &imxpd->encoder;
return 0; return 0;
} }
static int imx_pd_probe(struct platform_device *pdev) static int imx_pd_bind(struct device *dev, struct device *master, void *data)
{ {
struct device_node *np = pdev->dev.of_node; struct drm_device *drm = data;
struct device_node *np = dev->of_node;
const u8 *edidp; const u8 *edidp;
struct imx_parallel_display *imxpd; struct imx_parallel_display *imxpd;
int ret; int ret;
const char *fmt; const char *fmt;
imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL); imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
if (!imxpd) if (!imxpd)
return -ENOMEM; return -ENOMEM;
...@@ -225,30 +198,39 @@ static int imx_pd_probe(struct platform_device *pdev) ...@@ -225,30 +198,39 @@ static int imx_pd_probe(struct platform_device *pdev)
imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666; imxpd->interface_pix_fmt = V4L2_PIX_FMT_BGR666;
} }
imxpd->dev = &pdev->dev; imxpd->dev = dev;
ret = imx_pd_register(imxpd); ret = imx_pd_register(drm, imxpd);
if (ret) if (ret)
return ret; return ret;
ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np); dev_set_drvdata(dev, imxpd);
platform_set_drvdata(pdev, imxpd);
return 0; return 0;
} }
static int imx_pd_remove(struct platform_device *pdev) static void imx_pd_unbind(struct device *dev, struct device *master,
void *data)
{ {
struct imx_parallel_display *imxpd = platform_get_drvdata(pdev); struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
struct drm_connector *connector = &imxpd->connector;
struct drm_encoder *encoder = &imxpd->encoder;
drm_mode_connector_detach_encoder(connector, encoder); imxpd->encoder.funcs->destroy(&imxpd->encoder);
imxpd->connector.funcs->destroy(&imxpd->connector);
}
imx_drm_remove_connector(imxpd->imx_drm_connector); static const struct component_ops imx_pd_ops = {
imx_drm_remove_encoder(imxpd->imx_drm_encoder); .bind = imx_pd_bind,
.unbind = imx_pd_unbind,
};
static int imx_pd_probe(struct platform_device *pdev)
{
return component_add(&pdev->dev, &imx_pd_ops);
}
static int imx_pd_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &imx_pd_ops);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment