diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts index be1407cf5abd1b1479e55fbfeac48be44dc90d4c..6ff15a0eacb3ccfcb52ce450b189f6e0ebc51aa7 100644 --- a/arch/arm/boot/dts/imx51-babbage.dts +++ b/arch/arm/boot/dts/imx51-babbage.dts @@ -21,7 +21,7 @@ reg = <0x90000000 0x20000000>; }; - display@di0 { + display0: display@di0 { compatible = "fsl,imx-parallel-display"; crtcs = <&ipu 0>; interface-pix-fmt = "rgb24"; @@ -43,7 +43,7 @@ }; }; - display@di1 { + display1: display@di1 { compatible = "fsl,imx-parallel-display"; crtcs = <&ipu 1>; interface-pix-fmt = "rgb565"; @@ -81,6 +81,12 @@ }; }; + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu 0>, <&ipu 1>; + connectors = <&display0>, <&display1>; + }; + sound { compatible = "fsl,imx51-babbage-sgtl5000", "fsl,imx-audio-sgtl5000"; diff --git a/arch/arm/boot/dts/imx53-m53evk.dts b/arch/arm/boot/dts/imx53-m53evk.dts index 7d304d02ed384e744c4789e3329ccdd80e103ac6..ee6107b6484c64798caea5b1fefcc7c4e46eb6b6 100644 --- a/arch/arm/boot/dts/imx53-m53evk.dts +++ b/arch/arm/boot/dts/imx53-m53evk.dts @@ -21,7 +21,7 @@ }; soc { - display@di1 { + display1: display@di1 { compatible = "fsl,imx-parallel-display"; crtcs = <&ipu 1>; interface-pix-fmt = "bgr666"; @@ -53,6 +53,12 @@ default-brightness-level = <6>; }; + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu 1>; + connectors = <&display1>; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts index a630902679410f9a4f356ab430a3199bf3ac852b..9b6e76980a74c736def32e1926e9967312c9f390 100644 --- a/arch/arm/boot/dts/imx53-mba53.dts +++ b/arch/arm/boot/dts/imx53-mba53.dts @@ -43,6 +43,12 @@ status = "disabled"; }; + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu 1>; + connectors = <&disp1>, <&tve>; + }; + reg_3p2v: 3p2v { compatible = "regulator-fixed"; regulator-name = "3P2V"; diff --git a/arch/arm/boot/dts/imx53-qsb.dts b/arch/arm/boot/dts/imx53-qsb.dts index 91a5935a4aacd63879f2f2104f546d6f41bb7e60..3cb4f7791a9120c8ceb89afbe324904d3e0c49f2 100644 --- a/arch/arm/boot/dts/imx53-qsb.dts +++ b/arch/arm/boot/dts/imx53-qsb.dts @@ -21,7 +21,7 @@ reg = <0x70000000 0x40000000>; }; - display@di0 { + display0: display@di0 { compatible = "fsl,imx-parallel-display"; crtcs = <&ipu 0>; interface-pix-fmt = "rgb565"; @@ -72,6 +72,12 @@ }; }; + imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu 0>; + connectors = <&display0>; + }; + leds { compatible = "gpio-leds"; pinctrl-names = "default"; diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi index 9e8ae118fdd4e6c6c1df48a0b4a66c20e0504ae6..6dc39702221437f9cc60ea9d057fff311209f593 100644 --- a/arch/arm/boot/dts/imx6dl.dtsi +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -88,3 +88,8 @@ crtcs = <&ipu1 0>, <&ipu1 1>; }; }; + +&hdmi { + compatible = "fsl,imx6dl-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>; +}; diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts index 9cbdfe7a0931ff4fa8c09db3c9ea0fa3bb397609..66f220a82e4593b817fdee321bdf3fa63cfe4def 100644 --- a/arch/arm/boot/dts/imx6q-sabresd.dts +++ b/arch/arm/boot/dts/imx6q-sabresd.dts @@ -20,6 +20,10 @@ compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; }; +&imx_drm { + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +}; + &sata { status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index f024ef28b34b9373895dbbb913c475eebb33298a..187fe33ba515e86126a3447a0bdf518f00f4a46e 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -159,3 +159,8 @@ crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; }; }; + +&hdmi { + compatible = "fsl,imx6q-hdmi"; + crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>; +}; diff --git a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi index e75e11b36dffec5ea9e695c01d8f66ac35b83dee..dfca3e001398fa6e90a82a81e8e128291a440c86 100644 --- a/arch/arm/boot/dts/imx6qdl-sabresd.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sabresd.dtsi @@ -62,6 +62,12 @@ }; }; + imx_drm: imx-drm { + compatible = "fsl,imx-drm"; + crtcs = <&ipu1 0>, <&ipu1 1>; + connectors = <&ldb>; + }; + sound { compatible = "fsl,imx6q-sabresd-wm8962", "fsl,imx-audio-wm8962"; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index fb28b2ecb1db37a28a9effbd7bea591ace78554a..930ebe0c29370498fd847cda27093b050e1f9c53 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -1368,6 +1368,15 @@ }; }; + 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 { reg = <0x020e4000 0x4000>; interrupts = <0 124 0x04>; diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile index 4677585b5ad51287eba03f5b4238e545df31e880..129e3a3f59f100847231a90e2d8b7740ce4f4ce0 100644 --- a/drivers/staging/imx-drm/Makefile +++ b/drivers/staging/imx-drm/Makefile @@ -1,12 +1,11 @@ -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_PARALLEL_DISPLAY) += parallel-display.o obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.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/ imx-ipuv3-crtc-objs := ipuv3-crtc.o ipuv3-plane.o diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 236ed66f116a29fda3af7fc6764b506cacbef16a..dcba51853e8aaaaf4490b964b9156f5032ea0820 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -13,14 +13,14 @@ * GNU General Public License for more details. * */ - +#include #include +#include +#include #include #include #include #include -#include -#include #include #include @@ -28,45 +28,26 @@ #define MAX_CRTC 4 -struct crtc_cookie { - void *cookie; - int id; - struct list_head list; -}; +struct imx_drm_crtc; struct imx_drm_device { struct drm_device *drm; - struct device *dev; - struct list_head crtc_list; - struct list_head encoder_list; - struct list_head connector_list; - struct mutex mutex; + struct imx_drm_crtc *crtc[MAX_CRTC]; int pipes; struct drm_fbdev_cma *fbhelper; }; struct imx_drm_crtc { struct drm_crtc *crtc; - struct list_head list; - struct imx_drm_device *imxdrm; int pipe; struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; - struct module *owner; - struct crtc_cookie cookie; -}; - -struct imx_drm_encoder { - struct drm_encoder *encoder; - struct list_head list; - struct module *owner; - struct list_head possible_crtcs; + void *cookie; + int id; + int mux_id; }; -struct imx_drm_connector { - struct drm_connector *connector; - struct list_head list; - struct module *owner; -}; +static int legacyfb_depth = 16; +module_param(legacyfb_depth, int, 0444); int imx_drm_crtc_id(struct imx_drm_crtc *crtc) { @@ -76,69 +57,71 @@ EXPORT_SYMBOL_GPL(imx_drm_crtc_id); static void imx_drm_driver_lastclose(struct drm_device *drm) { +#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) struct imx_drm_device *imxdrm = drm->dev_private; if (imxdrm->fbhelper) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); +#endif } static int imx_drm_driver_unload(struct drm_device *drm) { +#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) struct imx_drm_device *imxdrm = drm->dev_private; +#endif - imx_drm_device_put(); + drm_kms_helper_poll_fini(drm); + +#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + if (imxdrm->fbhelper) + drm_fbdev_cma_fini(imxdrm->fbhelper); +#endif + + component_unbind_all(drm->dev, drm); drm_vblank_cleanup(drm); - drm_kms_helper_poll_fini(drm); drm_mode_config_cleanup(drm); return 0; } -/* - * We don't care at all for crtc numbers, but the core expects the - * crtcs to be numbered - */ -static struct imx_drm_crtc *imx_drm_crtc_by_num(struct imx_drm_device *imxdrm, - int num) +struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) { - struct imx_drm_crtc *imx_drm_crtc; + struct imx_drm_device *imxdrm = crtc->dev->dev_private; + unsigned i; + + for (i = 0; i < MAX_CRTC; i++) + if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc) + return imxdrm->crtc[i]; - list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) - if (imx_drm_crtc->pipe == num) - return imx_drm_crtc; return NULL; } -int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, +int imx_drm_panel_format_pins(struct drm_encoder *encoder, u32 interface_pix_fmt, int hsync_pin, int vsync_pin) { - struct imx_drm_device *imxdrm = crtc->dev->dev_private; - struct imx_drm_crtc *imx_crtc; struct imx_drm_crtc_helper_funcs *helper; + struct imx_drm_crtc *imx_crtc; - list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) - if (imx_crtc->crtc == crtc) - goto found; + imx_crtc = imx_drm_find_crtc(encoder->crtc); + if (!imx_crtc) + return -EINVAL; - return -EINVAL; -found: helper = &imx_crtc->imx_drm_helper_funcs; if (helper->set_interface_pix_fmt) - return helper->set_interface_pix_fmt(crtc, - encoder_type, interface_pix_fmt, + return helper->set_interface_pix_fmt(encoder->crtc, + encoder->encoder_type, interface_pix_fmt, hsync_pin, vsync_pin); return 0; } -EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format_pins); +EXPORT_SYMBOL_GPL(imx_drm_panel_format_pins); -int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type, - u32 interface_pix_fmt) +int imx_drm_panel_format(struct drm_encoder *encoder, u32 interface_pix_fmt) { - return imx_drm_crtc_panel_format_pins(crtc, encoder_type, - interface_pix_fmt, 2, 3); + return imx_drm_panel_format_pins(encoder, interface_pix_fmt, 2, 3); } -EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format); +EXPORT_SYMBOL_GPL(imx_drm_panel_format); int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc) { @@ -161,10 +144,9 @@ EXPORT_SYMBOL_GPL(imx_drm_handle_vblank); static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) { struct imx_drm_device *imxdrm = drm->dev_private; - struct imx_drm_crtc *imx_drm_crtc; + struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; int ret; - imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc); if (!imx_drm_crtc) return -EINVAL; @@ -180,9 +162,8 @@ static int imx_drm_enable_vblank(struct drm_device *drm, int crtc) static void imx_drm_disable_vblank(struct drm_device *drm, int crtc) { struct imx_drm_device *imxdrm = drm->dev_private; - struct imx_drm_crtc *imx_drm_crtc; + struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[crtc]; - imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc); if (!imx_drm_crtc) return; @@ -215,172 +196,54 @@ static const struct file_operations imx_drm_driver_fops = { .llseek = noop_llseek, }; -static struct imx_drm_device *imx_drm_device; - -static struct imx_drm_device *__imx_drm_device(void) +int imx_drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) { - return imx_drm_device; + return MODE_OK; } +EXPORT_SYMBOL(imx_drm_connector_mode_valid); -struct drm_device *imx_drm_device_get(void) +void imx_drm_connector_destroy(struct drm_connector *connector) { - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_encoder *enc; - struct imx_drm_connector *con; - struct imx_drm_crtc *crtc; - - list_for_each_entry(enc, &imxdrm->encoder_list, list) { - if (!try_module_get(enc->owner)) { - dev_err(imxdrm->dev, "could not get module %s\n", - module_name(enc->owner)); - goto unwind_enc; - } - } - - list_for_each_entry(con, &imxdrm->connector_list, list) { - if (!try_module_get(con->owner)) { - dev_err(imxdrm->dev, "could not get module %s\n", - module_name(con->owner)); - goto unwind_con; - } - } - - list_for_each_entry(crtc, &imxdrm->crtc_list, list) { - if (!try_module_get(crtc->owner)) { - dev_err(imxdrm->dev, "could not get module %s\n", - module_name(crtc->owner)); - goto unwind_crtc; - } - } - - return imxdrm->drm; - -unwind_crtc: - list_for_each_entry_continue_reverse(crtc, &imxdrm->crtc_list, list) - module_put(crtc->owner); -unwind_con: - list_for_each_entry_continue_reverse(con, &imxdrm->connector_list, list) - module_put(con->owner); -unwind_enc: - list_for_each_entry_continue_reverse(enc, &imxdrm->encoder_list, list) - module_put(enc->owner); - - mutex_unlock(&imxdrm->mutex); - - return NULL; - -} -EXPORT_SYMBOL_GPL(imx_drm_device_get); - -void imx_drm_device_put(void) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_encoder *enc; - struct imx_drm_connector *con; - struct imx_drm_crtc *crtc; - - mutex_lock(&imxdrm->mutex); - - list_for_each_entry(crtc, &imxdrm->crtc_list, list) - module_put(crtc->owner); - - list_for_each_entry(con, &imxdrm->connector_list, list) - module_put(con->owner); - - list_for_each_entry(enc, &imxdrm->encoder_list, list) - module_put(enc->owner); - - mutex_unlock(&imxdrm->mutex); -} -EXPORT_SYMBOL_GPL(imx_drm_device_put); - -static int drm_mode_group_reinit(struct drm_device *dev) -{ - struct drm_mode_group *group = &dev->primary->mode_group; - uint32_t *id_list = group->id_list; - int ret; - - ret = drm_mode_group_init_legacy_group(dev, group); - if (ret < 0) - return ret; - - kfree(id_list); - return 0; + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); } +EXPORT_SYMBOL_GPL(imx_drm_connector_destroy); -/* - * register an encoder to the drm core - */ -static int imx_drm_encoder_register(struct imx_drm_encoder *imx_drm_encoder) +void imx_drm_encoder_destroy(struct drm_encoder *encoder) { - struct imx_drm_device *imxdrm = __imx_drm_device(); - - INIT_LIST_HEAD(&imx_drm_encoder->possible_crtcs); - - drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder, - imx_drm_encoder->encoder->funcs, - imx_drm_encoder->encoder->encoder_type); - - drm_mode_group_reinit(imxdrm->drm); - - return 0; + drm_encoder_cleanup(encoder); } +EXPORT_SYMBOL_GPL(imx_drm_encoder_destroy); -/* - * unregister an encoder from the drm core - */ -static void imx_drm_encoder_unregister(struct imx_drm_encoder - *imx_drm_encoder) +static void imx_drm_output_poll_changed(struct drm_device *drm) { - struct imx_drm_device *imxdrm = __imx_drm_device(); - - drm_encoder_cleanup(imx_drm_encoder->encoder); - - drm_mode_group_reinit(imxdrm->drm); -} - -/* - * register a connector to the drm core - */ -static int imx_drm_connector_register( - struct imx_drm_connector *imx_drm_connector) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - - drm_connector_init(imxdrm->drm, imx_drm_connector->connector, - imx_drm_connector->connector->funcs, - imx_drm_connector->connector->connector_type); - drm_mode_group_reinit(imxdrm->drm); +#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + struct imx_drm_device *imxdrm = drm->dev_private; - return drm_sysfs_connector_add(imx_drm_connector->connector); + drm_fbdev_cma_hotplug_event(imxdrm->fbhelper); +#endif } -/* - * unregister a connector from the drm core - */ -static void imx_drm_connector_unregister( - struct imx_drm_connector *imx_drm_connector) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - - drm_sysfs_connector_remove(imx_drm_connector->connector); - drm_connector_cleanup(imx_drm_connector->connector); - - drm_mode_group_reinit(imxdrm->drm); -} +static struct drm_mode_config_funcs imx_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .output_poll_changed = imx_drm_output_poll_changed, +}; /* - * Called by the CRTC driver when all CRTCs are registered. This - * puts all the pieces together and initializes the driver. - * Once this is called no more CRTCs can be registered since - * the drm core has hardcoded the number of crtcs in several - * places. + * Main DRM initialisation. This binds, initialises and registers + * with DRM the subcomponents of the driver. */ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) { - struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_device *imxdrm; + struct drm_connector *connector; int ret; + imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL); + if (!imxdrm) + return -ENOMEM; + imxdrm->drm = drm; drm->dev_private = imxdrm; @@ -396,120 +259,123 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags) */ drm->irq_enabled = true; - drm_mode_config_init(drm); - imx_drm_mode_config_init(drm); - - mutex_lock(&imxdrm->mutex); - - drm_kms_helper_poll_init(drm); + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + drm->mode_config.min_width = 64; + drm->mode_config.min_height = 64; + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + drm->mode_config.funcs = &imx_drm_mode_config_funcs; - /* setup the grouping for the legacy output */ - ret = drm_mode_group_init_legacy_group(drm, - &drm->primary->mode_group); - if (ret) - goto err_kms; + drm_mode_config_init(drm); ret = drm_vblank_init(drm, MAX_CRTC); if (ret) goto err_kms; /* - * with vblank_disable_allowed = true, vblank interrupt will be disabled - * by drm timer once a current process gives up ownership of - * vblank event.(after drm_vblank_put function is called) + * with vblank_disable_allowed = true, vblank interrupt will be + * disabled by drm timer once a current process gives up ownership + * of vblank event. (after drm_vblank_put function is called) */ drm->vblank_disable_allowed = true; - if (!imx_drm_device_get()) { - ret = -EINVAL; + platform_set_drvdata(drm->platformdev, drm); + + /* Now try and bind all our sub-components */ + ret = component_bind_all(drm->dev, drm); + if (ret) goto err_vblank; + + /* + * All components are now added, we can publish the connector sysfs + * entries to userspace. This will generate hotplug events and so + * userspace will expect to be able to access DRM at this point. + */ + list_for_each_entry(connector, &drm->mode_config.connector_list, head) { + ret = drm_sysfs_connector_add(connector); + if (ret) { + dev_err(drm->dev, + "[CONNECTOR:%d:%s] drm_sysfs_connector_add failed: %d\n", + connector->base.id, + drm_get_connector_name(connector), ret); + goto err_unbind; + } } - platform_set_drvdata(drm->platformdev, drm); - mutex_unlock(&imxdrm->mutex); + /* + * All components are now initialised, so setup the fb helper. + * The fb helper takes copies of key hardware information, so the + * crtcs/connectors/encoders must not change after this point. + */ +#if IS_ENABLED(CONFIG_DRM_IMX_FB_HELPER) + if (legacyfb_depth != 16 && legacyfb_depth != 32) { + dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n"); + legacyfb_depth = 16; + } + imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, + drm->mode_config.num_crtc, MAX_CRTC); + if (IS_ERR(imxdrm->fbhelper)) { + ret = PTR_ERR(imxdrm->fbhelper); + imxdrm->fbhelper = NULL; + goto err_unbind; + } +#endif + + drm_kms_helper_poll_init(drm); + return 0; +err_unbind: + component_unbind_all(drm->dev, drm); err_vblank: drm_vblank_cleanup(drm); err_kms: - drm_kms_helper_poll_fini(drm); drm_mode_config_cleanup(drm); - mutex_unlock(&imxdrm->mutex); return ret; } -static void imx_drm_update_possible_crtcs(void) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_crtc *imx_drm_crtc; - struct imx_drm_encoder *enc; - struct crtc_cookie *cookie; - - list_for_each_entry(enc, &imxdrm->encoder_list, list) { - u32 possible_crtcs = 0; - - list_for_each_entry(cookie, &enc->possible_crtcs, list) { - list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) { - if (imx_drm_crtc->cookie.cookie == cookie->cookie && - imx_drm_crtc->cookie.id == cookie->id) { - possible_crtcs |= 1 << imx_drm_crtc->pipe; - } - } - } - enc->encoder->possible_crtcs = possible_crtcs; - enc->encoder->possible_clones = possible_crtcs; - } -} - /* * imx_drm_add_crtc - add a new crtc * * The return value if !NULL is a cookie for the caller to pass to * imx_drm_remove_crtc later. */ -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, const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs, - struct module *owner, void *cookie, int id) + void *cookie, int id) { - struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_device *imxdrm = drm->dev_private; struct imx_drm_crtc *imx_drm_crtc; int ret; - mutex_lock(&imxdrm->mutex); - /* * The vblank arrays are dimensioned by MAX_CRTC - we can't * pass IDs greater than this to those functions. */ - if (imxdrm->pipes >= MAX_CRTC) { - ret = -EINVAL; - goto err_busy; - } + if (imxdrm->pipes >= MAX_CRTC) + return -EINVAL; - if (imxdrm->drm->open_count) { - ret = -EBUSY; - goto err_busy; - } + if (imxdrm->drm->open_count) + return -EBUSY; imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL); - if (!imx_drm_crtc) { - ret = -ENOMEM; - goto err_alloc; - } + if (!imx_drm_crtc) + return -ENOMEM; imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; imx_drm_crtc->pipe = imxdrm->pipes++; - imx_drm_crtc->cookie.cookie = cookie; - imx_drm_crtc->cookie.id = id; - + imx_drm_crtc->cookie = cookie; + imx_drm_crtc->id = id; + imx_drm_crtc->mux_id = imx_drm_crtc->pipe; imx_drm_crtc->crtc = crtc; - imx_drm_crtc->imxdrm = imxdrm; - imx_drm_crtc->owner = owner; - - list_add_tail(&imx_drm_crtc->list, &imxdrm->crtc_list); + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc; *new_crtc = imx_drm_crtc; @@ -520,23 +386,14 @@ int imx_drm_add_crtc(struct drm_crtc *crtc, drm_crtc_helper_add(crtc, imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs); - drm_crtc_init(imxdrm->drm, crtc, + drm_crtc_init(drm, crtc, imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs); - drm_mode_group_reinit(imxdrm->drm); - - imx_drm_update_possible_crtcs(); - - mutex_unlock(&imxdrm->mutex); - return 0; err_register: - list_del(&imx_drm_crtc->list); + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; kfree(imx_drm_crtc); -err_alloc: -err_busy: - mutex_unlock(&imxdrm->mutex); return ret; } EXPORT_SYMBOL_GPL(imx_drm_add_crtc); @@ -546,17 +403,11 @@ EXPORT_SYMBOL_GPL(imx_drm_add_crtc); */ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) { - struct imx_drm_device *imxdrm = imx_drm_crtc->imxdrm; - - mutex_lock(&imxdrm->mutex); + struct imx_drm_device *imxdrm = imx_drm_crtc->crtc->dev->dev_private; drm_crtc_cleanup(imx_drm_crtc->crtc); - list_del(&imx_drm_crtc->list); - - drm_mode_group_reinit(imxdrm->drm); - - mutex_unlock(&imxdrm->mutex); + imxdrm->crtc[imx_drm_crtc->pipe] = NULL; kfree(imx_drm_crtc); @@ -565,220 +416,78 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) EXPORT_SYMBOL_GPL(imx_drm_remove_crtc); /* - * imx_drm_add_encoder - add a new encoder + * Find the DRM CRTC possible mask for the device node cookie/id. + * + * The encoder possible masks are defined by their position in the + * mode_config crtc_list. This means that CRTCs must not be added + * or removed once the DRM device has been fully initialised. */ -int imx_drm_add_encoder(struct drm_encoder *encoder, - struct imx_drm_encoder **newenc, struct module *owner) +static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, + void *cookie, int id) { - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_encoder *imx_drm_encoder; - int ret; - - mutex_lock(&imxdrm->mutex); - - if (imxdrm->drm->open_count) { - ret = -EBUSY; - goto err_busy; - } + unsigned i; - imx_drm_encoder = kzalloc(sizeof(*imx_drm_encoder), GFP_KERNEL); - if (!imx_drm_encoder) { - ret = -ENOMEM; - goto err_alloc; + for (i = 0; i < MAX_CRTC; i++) { + struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; + if (imx_drm_crtc && imx_drm_crtc->id == id && + imx_drm_crtc->cookie == cookie) + return drm_crtc_mask(imx_drm_crtc->crtc); } - imx_drm_encoder->encoder = encoder; - imx_drm_encoder->owner = owner; - - ret = imx_drm_encoder_register(imx_drm_encoder); - if (ret) { - ret = -ENOMEM; - goto err_register; - } - - list_add_tail(&imx_drm_encoder->list, &imxdrm->encoder_list); - - *newenc = imx_drm_encoder; - - mutex_unlock(&imxdrm->mutex); - return 0; - -err_register: - kfree(imx_drm_encoder); -err_alloc: -err_busy: - mutex_unlock(&imxdrm->mutex); - - return ret; } -EXPORT_SYMBOL_GPL(imx_drm_add_encoder); -int imx_drm_encoder_add_possible_crtcs( - struct imx_drm_encoder *imx_drm_encoder, - struct device_node *np) +int imx_drm_encoder_parse_of(struct drm_device *drm, + struct drm_encoder *encoder, struct device_node *np) { - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct of_phandle_args args; - struct crtc_cookie *c; - int ret = 0; - int i; - - if (!list_empty(&imx_drm_encoder->possible_crtcs)) - return -EBUSY; + struct imx_drm_device *imxdrm = drm->dev_private; + uint32_t crtc_mask = 0; + int i, ret = 0; for (i = 0; !ret; i++) { - ret = of_parse_phandle_with_args(np, "crtcs", - "#crtc-cells", i, &args); - if (ret < 0) - break; + struct of_phandle_args args; + uint32_t mask; + int id; - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - of_node_put(args.np); - return -ENOMEM; - } - - c->cookie = args.np; - c->id = args.args_count > 0 ? args.args[0] : 0; + ret = of_parse_phandle_with_args(np, "crtcs", "#crtc-cells", i, + &args); + if (ret == -ENOENT) + break; + if (ret < 0) + return ret; + id = args.args_count > 0 ? args.args[0] : 0; + mask = imx_drm_find_crtc_mask(imxdrm, args.np, id); of_node_put(args.np); - mutex_lock(&imxdrm->mutex); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (mask == 0) + return -EPROBE_DEFER; - list_add_tail(&c->list, &imx_drm_encoder->possible_crtcs); - - mutex_unlock(&imxdrm->mutex); + crtc_mask |= mask; } - imx_drm_update_possible_crtcs(); + encoder->possible_crtcs = crtc_mask; - return 0; -} -EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs); - -int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, - struct drm_crtc *crtc) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_crtc *imx_crtc; - int i = 0; - - list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) { - if (imx_crtc->crtc == crtc) - goto found; - i++; - } - - return -EINVAL; -found: - return i; -} -EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); - -/* - * imx_drm_remove_encoder - remove an encoder - */ -int imx_drm_remove_encoder(struct imx_drm_encoder *imx_drm_encoder) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct crtc_cookie *c, *tmp; - - mutex_lock(&imxdrm->mutex); - - imx_drm_encoder_unregister(imx_drm_encoder); - - list_del(&imx_drm_encoder->list); - - list_for_each_entry_safe(c, tmp, &imx_drm_encoder->possible_crtcs, - list) - kfree(c); - - mutex_unlock(&imxdrm->mutex); - - kfree(imx_drm_encoder); - - return 0; -} -EXPORT_SYMBOL_GPL(imx_drm_remove_encoder); - -/* - * imx_drm_add_connector - add a connector - */ -int imx_drm_add_connector(struct drm_connector *connector, - struct imx_drm_connector **new_con, - struct module *owner) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - struct imx_drm_connector *imx_drm_connector; - int ret; - - mutex_lock(&imxdrm->mutex); - - if (imxdrm->drm->open_count) { - ret = -EBUSY; - goto err_busy; - } - - imx_drm_connector = kzalloc(sizeof(*imx_drm_connector), GFP_KERNEL); - if (!imx_drm_connector) { - ret = -ENOMEM; - goto err_alloc; - } - - imx_drm_connector->connector = connector; - imx_drm_connector->owner = owner; - - ret = imx_drm_connector_register(imx_drm_connector); - if (ret) - goto err_register; - - list_add_tail(&imx_drm_connector->list, &imxdrm->connector_list); - - *new_con = imx_drm_connector; - - mutex_unlock(&imxdrm->mutex); + /* FIXME: this is the mask of outputs which can clone this output. */ + encoder->possible_clones = ~0; return 0; - -err_register: - kfree(imx_drm_connector); -err_alloc: -err_busy: - mutex_unlock(&imxdrm->mutex); - - return ret; } -EXPORT_SYMBOL_GPL(imx_drm_add_connector); +EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); -void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper) +int imx_drm_encoder_get_mux_id(struct drm_encoder *encoder) { - struct imx_drm_device *imxdrm = __imx_drm_device(); + struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); - imxdrm->fbhelper = fbdev_helper; + return imx_crtc ? imx_crtc->mux_id : -EINVAL; } -EXPORT_SYMBOL_GPL(imx_drm_fb_helper_set); - -/* - * imx_drm_remove_connector - remove a connector - */ -int imx_drm_remove_connector(struct imx_drm_connector *imx_drm_connector) -{ - struct imx_drm_device *imxdrm = __imx_drm_device(); - - mutex_lock(&imxdrm->mutex); - - imx_drm_connector_unregister(imx_drm_connector); - - list_del(&imx_drm_connector->list); - - mutex_unlock(&imxdrm->mutex); - - kfree(imx_drm_connector); - - return 0; -} -EXPORT_SYMBOL_GPL(imx_drm_remove_connector); +EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); static const struct drm_ioctl_desc imx_drm_ioctls[] = { /* none so far */ @@ -819,80 +528,103 @@ static struct drm_driver imx_drm_driver = { .patchlevel = 0, }; -static int imx_drm_platform_probe(struct platform_device *pdev) +static int compare_parent_of(struct device *dev, void *data) { - int ret; - - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - imx_drm_device->dev = &pdev->dev; - - return drm_platform_init(&imx_drm_driver, pdev); + struct of_phandle_args *args = data; + return dev->parent && dev->parent->of_node == args->np; } -static int imx_drm_platform_remove(struct platform_device *pdev) +static int compare_of(struct device *dev, void *data) { - drm_put_dev(platform_get_drvdata(pdev)); - - return 0; + return dev->of_node == data; } -static struct platform_driver imx_drm_pdrv = { - .probe = imx_drm_platform_probe, - .remove = imx_drm_platform_remove, - .driver = { - .owner = THIS_MODULE, - .name = "imx-drm", - }, -}; - -static struct platform_device *imx_drm_pdev; - -static int __init imx_drm_init(void) +static int imx_drm_add_components(struct device *master, struct master *m) { + struct device_node *np = master->of_node; + unsigned i; int ret; - imx_drm_device = kzalloc(sizeof(*imx_drm_device), GFP_KERNEL); - if (!imx_drm_device) - return -ENOMEM; + for (i = 0; ; i++) { + struct of_phandle_args args; + + ret = of_parse_phandle_with_fixed_args(np, "crtcs", 1, + i, &args); + if (ret) + break; - mutex_init(&imx_drm_device->mutex); - INIT_LIST_HEAD(&imx_drm_device->crtc_list); - INIT_LIST_HEAD(&imx_drm_device->connector_list); - INIT_LIST_HEAD(&imx_drm_device->encoder_list); + ret = component_master_add_child(m, compare_parent_of, &args); + of_node_put(args.np); - imx_drm_pdev = platform_device_register_simple("imx-drm", -1, NULL, 0); - if (IS_ERR(imx_drm_pdev)) { - ret = PTR_ERR(imx_drm_pdev); - goto err_pdev; + if (ret) + return ret; } - ret = platform_driver_register(&imx_drm_pdrv); - if (ret) - goto err_pdrv; + for (i = 0; ; i++) { + struct device_node *node; + + node = of_parse_phandle(np, "connectors", i); + if (!node) + break; + + ret = component_master_add_child(m, compare_of, node); + of_node_put(node); + if (ret) + return ret; + } return 0; +} -err_pdrv: - platform_device_unregister(imx_drm_pdev); -err_pdev: - kfree(imx_drm_device); +static int imx_drm_bind(struct device *dev) +{ + return drm_platform_init(&imx_drm_driver, to_platform_device(dev)); +} - return ret; +static void imx_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); } -static void __exit imx_drm_exit(void) +static const struct component_master_ops imx_drm_ops = { + .add_components = imx_drm_add_components, + .bind = imx_drm_bind, + .unbind = imx_drm_unbind, +}; + +static int imx_drm_platform_probe(struct platform_device *pdev) { - platform_device_unregister(imx_drm_pdev); - platform_driver_unregister(&imx_drm_pdrv); + int ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; - kfree(imx_drm_device); + return component_master_add(&pdev->dev, &imx_drm_ops); } -module_init(imx_drm_init); -module_exit(imx_drm_exit); +static int imx_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &imx_drm_ops); + return 0; +} + +static const struct of_device_id imx_drm_dt_ids[] = { + { .compatible = "fsl,imx-drm", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); + +static struct platform_driver imx_drm_pdrv = { + .probe = imx_drm_platform_probe, + .remove = imx_drm_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = "imx-drm", + .of_match_table = imx_drm_dt_ids, + }, +}; +module_platform_driver(imx_drm_pdrv); MODULE_AUTHOR("Sascha Hauer "); MODULE_DESCRIPTION("i.MX drm driver core"); diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h index ae90c9c15312c2ca57fb1884b622a8bf0469cf0e..aa21028666896765430cd772eda18fecbb5b7be1 100644 --- a/drivers/staging/imx-drm/imx-drm.h +++ b/drivers/staging/imx-drm/imx-drm.h @@ -5,13 +5,15 @@ #define IPU_PIX_FMT_GBR24 v4l2_fourcc('G', 'B', 'R', '3') +struct device_node; struct drm_crtc; struct drm_connector; struct drm_device; +struct drm_display_mode; struct drm_encoder; -struct imx_drm_crtc; struct drm_fbdev_cma; struct drm_framebuffer; +struct imx_drm_crtc; struct platform_device; int imx_drm_crtc_id(struct imx_drm_crtc *crtc); @@ -25,10 +27,10 @@ struct imx_drm_crtc_helper_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, 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_init_drm(struct platform_device *pdev, int preferred_bpp); @@ -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_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); struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); -struct drm_device *imx_drm_device_get(void); -void imx_drm_device_put(void); -int imx_drm_crtc_panel_format_pins(struct drm_crtc *crtc, u32 encoder_type, +int imx_drm_panel_format_pins(struct drm_encoder *encoder, 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); -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, - struct drm_crtc *crtc); -int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder, - struct device_node *np); +int imx_drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); +void imx_drm_connector_destroy(struct drm_connector *connector); +void imx_drm_encoder_destroy(struct drm_encoder *encoder); #endif /* _IMX_DRM_H_ */ diff --git a/drivers/staging/imx-drm/imx-fb.c b/drivers/staging/imx-drm/imx-fb.c deleted file mode 100644 index 03a7b4e14f673adebd6f7c1d0122ee562aa56be0..0000000000000000000000000000000000000000 --- a/drivers/staging/imx-drm/imx-fb.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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; -} diff --git a/drivers/staging/imx-drm/imx-fbdev.c b/drivers/staging/imx-drm/imx-fbdev.c deleted file mode 100644 index 8331739c3d085ca00a6639da41891fb614b23429..0000000000000000000000000000000000000000 --- a/drivers/staging/imx-drm/imx-fbdev.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -#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"); diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c index f1aa48cb500f0e6b9ce517ab0e0952f6f1bde645..8384ceab932ae61109946bfccc7cf8c5a9eab3a9 100644 --- a/drivers/staging/imx-drm/imx-hdmi.c +++ b/drivers/staging/imx-drm/imx-hdmi.c @@ -12,6 +12,7 @@ * Copyright (C) 2010, Guennadi Liakhovetski */ +#include #include #include #include @@ -112,15 +113,15 @@ struct hdmi_data_info { struct imx_hdmi { struct drm_connector connector; - struct imx_drm_connector *imx_drm_connector; struct drm_encoder encoder; - struct imx_drm_encoder *imx_drm_encoder; enum imx_hdmi_devtype dev_type; struct device *dev; struct clk *isfr_clk; struct clk *iahb_clk; + enum drm_connector_status connector_status; + struct hdmi_data_info hdmi_data; int vic; @@ -134,7 +135,6 @@ struct imx_hdmi { struct i2c_adapter *ddc; void __iomem *regs; - unsigned long pixel_clk_rate; unsigned int sample_rate; int ratio; }; @@ -156,37 +156,34 @@ static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset) return readb(hdmi->regs + offset); } +static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg) +{ + u8 val = hdmi_readb(hdmi, reg) & ~mask; + val |= data & mask; + hdmi_writeb(hdmi, val, reg); +} + static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg, u8 shift, u8 mask) { - u8 value = hdmi_readb(hdmi, reg) & ~mask; - value |= (data << shift) & mask; - hdmi_writeb(hdmi, value, reg); + hdmi_modb(hdmi, data << shift, mask, reg); } static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi, unsigned int value) { - u8 val; - hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1); hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2); hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3); /* nshift factor = 0 */ - val = hdmi_readb(hdmi, HDMI_AUD_CTS3); - val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK; - hdmi_writeb(hdmi, val, HDMI_AUD_CTS3); + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3); } static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts) { - u8 val; - /* Must be set/cleared first */ - val = hdmi_readb(hdmi, HDMI_AUD_CTS3); - val &= ~HDMI_AUD_CTS3_CTS_MANUAL; - hdmi_writeb(hdmi, val, HDMI_AUD_CTS3); + hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1); hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2); @@ -331,34 +328,25 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, return (cts * ratio) / 100; } -static void hdmi_get_pixel_clk(struct imx_hdmi *hdmi) -{ - unsigned long rate; - - rate = 65000000; /* FIXME */ - - if (rate) - hdmi->pixel_clk_rate = rate; -} - -static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi) +static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi, + unsigned long pixel_clk) { unsigned int clk_n, clk_cts; - clk_n = hdmi_compute_n(hdmi->sample_rate, hdmi->pixel_clk_rate, + clk_n = hdmi_compute_n(hdmi->sample_rate, pixel_clk, hdmi->ratio); - clk_cts = hdmi_compute_cts(hdmi->sample_rate, hdmi->pixel_clk_rate, + clk_cts = hdmi_compute_cts(hdmi->sample_rate, pixel_clk, hdmi->ratio); if (!clk_cts) { dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", - __func__, hdmi->pixel_clk_rate); + __func__, pixel_clk); return; } dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", __func__, hdmi->sample_rate, hdmi->ratio, - hdmi->pixel_clk_rate, clk_n, clk_cts); + pixel_clk, clk_n, clk_cts); hdmi_set_clock_regenerator_n(hdmi, clk_n); hdmi_regenerate_cts(hdmi, clk_cts); @@ -366,32 +354,12 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi) static void hdmi_init_clk_regenerator(struct imx_hdmi *hdmi) { - unsigned int clk_n, clk_cts; - - clk_n = hdmi_compute_n(hdmi->sample_rate, hdmi->pixel_clk_rate, - hdmi->ratio); - clk_cts = hdmi_compute_cts(hdmi->sample_rate, hdmi->pixel_clk_rate, - hdmi->ratio); - - if (!clk_cts) { - dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n", - __func__, hdmi->pixel_clk_rate); - return; - } - - dev_dbg(hdmi->dev, "%s: samplerate=%d ratio=%d pixelclk=%lu N=%d cts=%d\n", - __func__, hdmi->sample_rate, hdmi->ratio, - hdmi->pixel_clk_rate, clk_n, clk_cts); - - hdmi_set_clock_regenerator_n(hdmi, clk_n); - hdmi_regenerate_cts(hdmi, clk_cts); + hdmi_set_clk_regenerator(hdmi, 74250000); } static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi) { - /* Get pixel clock from ipu */ - hdmi_get_pixel_clk(hdmi); - hdmi_set_clk_regenerator(hdmi); + hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock); } /* @@ -485,8 +453,8 @@ static int is_color_space_interpolation(struct imx_hdmi *hdmi) static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) { const u16 (*csc_coeff)[3][4] = &csc_coeff_default; + unsigned i; u32 csc_scale = 1; - u8 val; if (is_color_space_conversion(hdmi)) { if (hdmi->hdmi_data.enc_out_format == RGB) { @@ -503,37 +471,22 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi) } } - hdmi_writeb(hdmi, ((*csc_coeff)[0][0] & 0xff), HDMI_CSC_COEF_A1_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][0] >> 8), HDMI_CSC_COEF_A1_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][1] & 0xff), HDMI_CSC_COEF_A2_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][1] >> 8), HDMI_CSC_COEF_A2_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][2] & 0xff), HDMI_CSC_COEF_A3_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][2] >> 8), HDMI_CSC_COEF_A3_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][3] & 0xff), HDMI_CSC_COEF_A4_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[0][3] >> 8), HDMI_CSC_COEF_A4_MSB); - - hdmi_writeb(hdmi, ((*csc_coeff)[1][0] & 0xff), HDMI_CSC_COEF_B1_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][0] >> 8), HDMI_CSC_COEF_B1_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][1] & 0xff), HDMI_CSC_COEF_B2_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][1] >> 8), HDMI_CSC_COEF_B2_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][2] & 0xff), HDMI_CSC_COEF_B3_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][2] >> 8), HDMI_CSC_COEF_B3_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][3] & 0xff), HDMI_CSC_COEF_B4_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[1][3] >> 8), HDMI_CSC_COEF_B4_MSB); - - hdmi_writeb(hdmi, ((*csc_coeff)[2][0] & 0xff), HDMI_CSC_COEF_C1_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][0] >> 8), HDMI_CSC_COEF_C1_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][1] & 0xff), HDMI_CSC_COEF_C2_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][1] >> 8), HDMI_CSC_COEF_C2_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][2] & 0xff), HDMI_CSC_COEF_C3_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][2] >> 8), HDMI_CSC_COEF_C3_MSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][3] & 0xff), HDMI_CSC_COEF_C4_LSB); - hdmi_writeb(hdmi, ((*csc_coeff)[2][3] >> 8), HDMI_CSC_COEF_C4_MSB); - - val = hdmi_readb(hdmi, HDMI_CSC_SCALE); - val &= ~HDMI_CSC_SCALE_CSCSCALE_MASK; - val |= csc_scale & HDMI_CSC_SCALE_CSCSCALE_MASK; - hdmi_writeb(hdmi, val, HDMI_CSC_SCALE); + /* The CSC registers are sequential, alternating MSB then LSB */ + for (i = 0; i < ARRAY_SIZE(csc_coeff_default[0]); i++) { + u16 coeff_a = (*csc_coeff)[0][i]; + u16 coeff_b = (*csc_coeff)[1][i]; + u16 coeff_c = (*csc_coeff)[2][i]; + + hdmi_writeb(hdmi, coeff_a & 0xff, HDMI_CSC_COEF_A1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2); + hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2); + hdmi_writeb(hdmi, coeff_c & 0xff, HDMI_CSC_COEF_C1_LSB + i * 2); + hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2); + } + + hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK, + HDMI_CSC_SCALE); } static void hdmi_video_csc(struct imx_hdmi *hdmi) @@ -541,7 +494,6 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) int color_depth = 0; int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; int decimation = 0; - u8 val; /* YCC422 interpolation to 444 mode */ if (is_color_space_interpolation(hdmi)) @@ -562,10 +514,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi) /* Configure the CSC registers */ hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); - val = hdmi_readb(hdmi, HDMI_CSC_SCALE); - val &= ~HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK; - val |= color_depth; - hdmi_writeb(hdmi, val, HDMI_CSC_SCALE); + hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK, + HDMI_CSC_SCALE); imx_hdmi_update_csc_coeffs(hdmi); } @@ -581,7 +531,7 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; - u8 val; + u8 val, vp_conf; if (hdmi_data->enc_out_format == RGB || hdmi_data->enc_out_format == YCBCR444) { @@ -620,107 +570,75 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi) HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); hdmi_writeb(hdmi, val, HDMI_VP_PR_CD); - val = hdmi_readb(hdmi, HDMI_VP_STUFF); - val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK; - val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE; - hdmi_writeb(hdmi, val, HDMI_VP_STUFF); + hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE, + HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF); /* Data from pixel repeater block */ if (hdmi_data->pix_repet_factor > 1) { - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~(HDMI_VP_CONF_PR_EN_MASK | - HDMI_VP_CONF_BYPASS_SELECT_MASK); - val |= HDMI_VP_CONF_PR_EN_ENABLE | - HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + vp_conf = HDMI_VP_CONF_PR_EN_ENABLE | + HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; } else { /* data from packetizer block */ - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~(HDMI_VP_CONF_PR_EN_MASK | - HDMI_VP_CONF_BYPASS_SELECT_MASK); - val |= HDMI_VP_CONF_PR_EN_DISABLE | - HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + vp_conf = HDMI_VP_CONF_PR_EN_DISABLE | + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; } - val = hdmi_readb(hdmi, HDMI_VP_STUFF); - val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK; - val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET; - hdmi_writeb(hdmi, val, HDMI_VP_STUFF); + hdmi_modb(hdmi, vp_conf, + HDMI_VP_CONF_PR_EN_MASK | + HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF); + + hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET, + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF); hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP); if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | - HDMI_VP_CONF_PP_EN_ENMASK | - HDMI_VP_CONF_YCC422_EN_MASK); - val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | - HDMI_VP_CONF_PP_EN_ENABLE | - HDMI_VP_CONF_YCC422_EN_DISABLE; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_ENABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | - HDMI_VP_CONF_PP_EN_ENMASK | - HDMI_VP_CONF_YCC422_EN_MASK); - val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | - HDMI_VP_CONF_PP_EN_DISABLE | - HDMI_VP_CONF_YCC422_EN_ENABLE; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_ENABLE; } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | - HDMI_VP_CONF_PP_EN_ENMASK | - HDMI_VP_CONF_YCC422_EN_MASK); - val |= HDMI_VP_CONF_BYPASS_EN_ENABLE | - HDMI_VP_CONF_PP_EN_DISABLE | - HDMI_VP_CONF_YCC422_EN_DISABLE; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + vp_conf = HDMI_VP_CONF_BYPASS_EN_ENABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; } else { return; } - val = hdmi_readb(hdmi, HDMI_VP_STUFF); - val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK | - HDMI_VP_STUFF_YCC422_STUFFING_MASK); - val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | - HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE; - hdmi_writeb(hdmi, val, HDMI_VP_STUFF); + hdmi_modb(hdmi, vp_conf, + HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK | + HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF); + + hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE, + HDMI_VP_STUFF_PP_STUFFING_MASK | + HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF); - val = hdmi_readb(hdmi, HDMI_VP_CONF); - val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK; - val |= output_select; - hdmi_writeb(hdmi, val, HDMI_VP_CONF); + hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK, + HDMI_VP_CONF); } static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi, unsigned char bit) { - u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); - val &= ~HDMI_PHY_TST0_TSTCLR_MASK; - val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & - HDMI_PHY_TST0_TSTCLR_MASK; - hdmi_writeb(hdmi, val, HDMI_PHY_TST0); + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET, + HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0); } static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi, unsigned char bit) { - u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); - val &= ~HDMI_PHY_TST0_TSTEN_MASK; - val |= (bit << HDMI_PHY_TST0_TSTEN_OFFSET) & - HDMI_PHY_TST0_TSTEN_MASK; - hdmi_writeb(hdmi, val, HDMI_PHY_TST0); + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET, + HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0); } static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi, unsigned char bit) { - u8 val = hdmi_readb(hdmi, HDMI_PHY_TST0); - val &= ~HDMI_PHY_TST0_TSTCLK_MASK; - val |= (bit << HDMI_PHY_TST0_TSTCLK_OFFSET) & - HDMI_PHY_TST0_TSTCLK_MASK; - hdmi_writeb(hdmi, val, HDMI_PHY_TST0); + hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET, + HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0); } static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi, @@ -811,19 +729,94 @@ static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable) HDMI_PHY_CONF0_SELDIPIF_MASK); } +enum { + RES_8, + RES_10, + RES_12, + RES_MAX, +}; + +struct mpll_config { + unsigned long mpixelclock; + struct { + u16 cpce; + u16 gmp; + } res[RES_MAX]; +}; + +static const struct mpll_config mpll_config[] = { + { + 45250000, { + { 0x01e0, 0x0000 }, + { 0x21e1, 0x0000 }, + { 0x41e2, 0x0000 } + }, + }, { + 92500000, { + { 0x0140, 0x0005 }, + { 0x2141, 0x0005 }, + { 0x4142, 0x0005 }, + }, + }, { + 148500000, { + { 0x00a0, 0x000a }, + { 0x20a1, 0x000a }, + { 0x40a2, 0x000a }, + }, + }, { + ~0UL, { + { 0x00a0, 0x000a }, + { 0x2001, 0x000f }, + { 0x4002, 0x000f }, + }, + } +}; + +struct curr_ctrl { + unsigned long mpixelclock; + u16 curr[RES_MAX]; +}; + +static const struct curr_ctrl curr_ctrl[] = { + /* pixelclk bpp8 bpp10 bpp12 */ + { + 54000000, { 0x091c, 0x091c, 0x06dc }, + }, { + 58400000, { 0x091c, 0x06dc, 0x06dc }, + }, { + 72000000, { 0x06dc, 0x06dc, 0x091c }, + }, { + 74250000, { 0x06dc, 0x0b5c, 0x091c }, + }, { + 118800000, { 0x091c, 0x091c, 0x06dc }, + }, { + 216000000, { 0x06dc, 0x0b5c, 0x091c }, + } +}; + static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, unsigned char res, int cscon) { + unsigned res_idx, i; u8 val, msec; - /* color resolution 0 is 8 bit colour depth */ - if (!res) - res = 8; - if (prep) return -EINVAL; - else if (res != 8 && res != 12) + + switch (res) { + case 0: /* color resolution 0 is 8 bit colour depth */ + case 8: + res_idx = RES_8; + break; + case 10: + res_idx = RES_10; + break; + case 12: + res_idx = RES_12; + break; + default: return -EINVAL; + } /* Enable csc path */ if (cscon) @@ -850,165 +843,30 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep, HDMI_PHY_I2CM_SLAVE_ADDR); hdmi_phy_test_clear(hdmi, 0); - if (hdmi->hdmi_data.video_mode.mpixelclock <= 45250000) { - switch (res) { - case 8: - /* PLL/MPLL Cfg */ - hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); /* GMPCTRL */ - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x21e1, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x41e2, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 92500000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x0140, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x2141, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x4142, 0x06); - hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 148500000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); + /* PLL/MPLL Cfg - always match on final entry */ + for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + mpll_config[i].mpixelclock) break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x20a1, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); - default: - return -EINVAL; - } - } else { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x2001, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x4002, 0x06); - hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); - default: - return -EINVAL; - } - } - if (hdmi->hdmi_data.video_mode.mpixelclock <= 54000000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); /* CURRCTRL */ - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 58400000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 72000000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 74250000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 118800000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - default: - return -EINVAL; - } - } else if (hdmi->hdmi_data.video_mode.mpixelclock <= 216000000) { - switch (res) { - case 8: - hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); - break; - case 10: - hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); - break; - case 12: - hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06); + hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15); + + for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++) + if (hdmi->hdmi_data.video_mode.mpixelclock <= + curr_ctrl[i].mpixelclock) break; - default: - return -EINVAL; - } - } else { + + if (i >= ARRAY_SIZE(curr_ctrl)) { dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n", hdmi->hdmi_data.video_mode.mpixelclock); return -EINVAL; } + /* CURRCTRL */ + hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10); + hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); /* RESISTANCE TERM 133Ohm Cfg */ @@ -1077,7 +935,7 @@ static int imx_hdmi_phy_init(struct imx_hdmi *hdmi) static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) { - u8 de, val; + u8 de; if (hdmi->hdmi_data.video_mode.mdataenablepolarity) de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; @@ -1085,20 +943,13 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi) de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; /* disable rx detect */ - val = hdmi_readb(hdmi, HDMI_A_HDCPCFG0); - val &= HDMI_A_HDCPCFG0_RXDETECT_MASK; - val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE; - hdmi_writeb(hdmi, val, HDMI_A_HDCPCFG0); + hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE, + HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0); - val = hdmi_readb(hdmi, HDMI_A_VIDPOLCFG); - val &= HDMI_A_VIDPOLCFG_DATAENPOL_MASK; - val |= de; - hdmi_writeb(hdmi, val, HDMI_A_VIDPOLCFG); + hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG); - val = hdmi_readb(hdmi, HDMI_A_HDCPCFG1); - val &= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK; - val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE; - hdmi_writeb(hdmi, val, HDMI_A_HDCPCFG1); + hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1); } static void hdmi_config_AVI(struct imx_hdmi *hdmi) @@ -1322,11 +1173,7 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi) static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi) { - u8 clkdis; - - clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS); - clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; - hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS); + hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS); } /* Workaround to clear the overflow condition */ @@ -1461,9 +1308,6 @@ static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi) /* Clear Hotplug interrupts */ hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); - /* Unmute interrupts */ - hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); - return 0; } @@ -1532,12 +1376,9 @@ static void imx_hdmi_poweroff(struct imx_hdmi *hdmi) static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector *connector, bool force) { - /* FIXME */ - return connector_status_connected; -} - -static void imx_hdmi_connector_destroy(struct drm_connector *connector) -{ + struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi, + connector); + return hdmi->connector_status; } static int imx_hdmi_connector_get_modes(struct drm_connector *connector) @@ -1565,13 +1406,6 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector) return 0; } -static int imx_hdmi_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - - return MODE_OK; -} - static struct drm_encoder *imx_hdmi_connector_best_encoder(struct drm_connector *connector) { @@ -1619,28 +1453,21 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder) struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); imx_hdmi_poweroff(hdmi); - imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE, - V4L2_PIX_FMT_RGB24); + imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24); } static void imx_hdmi_encoder_commit(struct drm_encoder *encoder) { struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); - int mux = imx_drm_encoder_get_mux_id(hdmi->imx_drm_encoder, - encoder->crtc); + int mux = imx_drm_encoder_get_mux_id(encoder); imx_hdmi_set_ipu_di_mux(hdmi, mux); imx_hdmi_poweron(hdmi); } -static void imx_hdmi_encoder_destroy(struct drm_encoder *encoder) -{ - return; -} - static struct drm_encoder_funcs imx_hdmi_encoder_funcs = { - .destroy = imx_hdmi_encoder_destroy, + .destroy = imx_drm_encoder_destroy, }; static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = { @@ -1656,21 +1483,32 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = imx_hdmi_connector_detect, - .destroy = imx_hdmi_connector_destroy, + .destroy = imx_drm_connector_destroy, }; static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = { .get_modes = imx_hdmi_connector_get_modes, - .mode_valid = imx_hdmi_connector_mode_valid, + .mode_valid = imx_drm_connector_mode_valid, .best_encoder = imx_hdmi_connector_best_encoder, }; +static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id) +{ + struct imx_hdmi *hdmi = dev_id; + u8 intr_stat; + + intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); + if (intr_stat) + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + + return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE; +} + static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) { struct imx_hdmi *hdmi = dev_id; u8 intr_stat; u8 phy_int_pol; - u8 val; intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0); @@ -1680,55 +1518,46 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id) if (phy_int_pol & HDMI_PHY_HPD) { dev_dbg(hdmi->dev, "EVENT=plugin\n"); - val = hdmi_readb(hdmi, HDMI_PHY_POL0); - val &= ~HDMI_PHY_HPD; - hdmi_writeb(hdmi, val, HDMI_PHY_POL0); + hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0); + hdmi->connector_status = connector_status_connected; imx_hdmi_poweron(hdmi); } else { dev_dbg(hdmi->dev, "EVENT=plugout\n"); - val = hdmi_readb(hdmi, HDMI_PHY_POL0); - val |= HDMI_PHY_HPD; - hdmi_writeb(hdmi, val, HDMI_PHY_POL0); + hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, HDMI_PHY_POL0); + hdmi->connector_status = connector_status_disconnected; imx_hdmi_poweroff(hdmi); } + drm_helper_hpd_irq_event(hdmi->connector.dev); } hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); return IRQ_HANDLED; } -static int imx_hdmi_register(struct imx_hdmi *hdmi) +static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi) { int ret; - hdmi->connector.funcs = &imx_hdmi_connector_funcs; - hdmi->encoder.funcs = &imx_hdmi_encoder_funcs; + ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder, + hdmi->dev->of_node); + if (ret) + return ret; - hdmi->encoder.encoder_type = DRM_MODE_ENCODER_TMDS; - hdmi->connector.connector_type = DRM_MODE_CONNECTOR_HDMIA; + hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs); - ret = imx_drm_add_encoder(&hdmi->encoder, &hdmi->imx_drm_encoder, - THIS_MODULE); - if (ret) { - dev_err(hdmi->dev, "adding encoder failed: %d\n", ret); - return ret; - } + drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS); drm_connector_helper_add(&hdmi->connector, &imx_hdmi_connector_helper_funcs); - - ret = imx_drm_add_connector(&hdmi->connector, - &hdmi->imx_drm_connector, THIS_MODULE); - if (ret) { - imx_drm_remove_encoder(hdmi->imx_drm_encoder); - dev_err(hdmi->dev, "adding connector failed: %d\n", ret); - return ret; - } + drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); hdmi->connector.encoder = &hdmi->encoder; @@ -1755,21 +1584,26 @@ static const struct of_device_id imx_hdmi_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); -static int imx_hdmi_platform_probe(struct platform_device *pdev) +static int imx_hdmi_bind(struct device *dev, struct device *master, void *data) { + struct platform_device *pdev = to_platform_device(dev); const struct of_device_id *of_id = - of_match_device(imx_hdmi_dt_ids, &pdev->dev); - struct device_node *np = pdev->dev.of_node; + of_match_device(imx_hdmi_dt_ids, dev); + struct drm_device *drm = data; + struct device_node *np = dev->of_node; struct device_node *ddc_node; struct imx_hdmi *hdmi; struct resource *iores; int ret, irq; - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; - hdmi->dev = &pdev->dev; + hdmi->dev = dev; + hdmi->connector_status = connector_status_disconnected; + hdmi->sample_rate = 48000; + hdmi->ratio = 100; if (of_id) { const struct platform_device_id *device_id = of_id->data; @@ -1791,13 +1625,14 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) if (irq < 0) return -EINVAL; - ret = devm_request_irq(&pdev->dev, irq, imx_hdmi_irq, 0, - dev_name(&pdev->dev), hdmi); + ret = devm_request_threaded_irq(dev, irq, imx_hdmi_hardirq, + imx_hdmi_irq, IRQF_SHARED, + dev_name(dev), hdmi); if (ret) return ret; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->regs = devm_ioremap_resource(&pdev->dev, iores); + hdmi->regs = devm_ioremap_resource(dev, iores); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); @@ -1836,7 +1671,7 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) } /* Product and revision IDs */ - dev_info(&pdev->dev, + dev_info(dev, "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", hdmi_readb(hdmi, HDMI_DESIGN_ID), hdmi_readb(hdmi, HDMI_REVISION_ID), @@ -1864,13 +1699,14 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) if (ret) goto err_iahb; - ret = imx_hdmi_register(hdmi); + ret = imx_hdmi_register(drm, hdmi); if (ret) goto err_iahb; - imx_drm_encoder_add_possible_crtcs(hdmi->imx_drm_encoder, np); + /* Unmute interrupts */ + hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); - platform_set_drvdata(pdev, hdmi); + dev_set_drvdata(dev, hdmi); return 0; @@ -1882,20 +1718,35 @@ static int imx_hdmi_platform_probe(struct platform_device *pdev) return ret; } -static int imx_hdmi_platform_remove(struct platform_device *pdev) +static void imx_hdmi_unbind(struct device *dev, struct device *master, + void *data) { - struct imx_hdmi *hdmi = platform_get_drvdata(pdev); - struct drm_connector *connector = &hdmi->connector; - struct drm_encoder *encoder = &hdmi->encoder; + struct imx_hdmi *hdmi = dev_get_drvdata(dev); - drm_mode_connector_detach_encoder(connector, encoder); - imx_drm_remove_connector(hdmi->imx_drm_connector); - imx_drm_remove_encoder(hdmi->imx_drm_encoder); + /* Disable all interrupts */ + hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); + + hdmi->connector.funcs->destroy(&hdmi->connector); + hdmi->encoder.funcs->destroy(&hdmi->encoder); clk_disable_unprepare(hdmi->iahb_clk); clk_disable_unprepare(hdmi->isfr_clk); i2c_put_adapter(hdmi->ddc); +} + +static const struct component_ops hdmi_ops = { + .bind = imx_hdmi_bind, + .unbind = imx_hdmi_unbind, +}; + +static int imx_hdmi_platform_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &hdmi_ops); +} +static int imx_hdmi_platform_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &hdmi_ops); return 0; } diff --git a/drivers/staging/imx-drm/imx-ldb.c b/drivers/staging/imx-drm/imx-ldb.c index 7e593296ac47287f4929b91270ff79d34752fd56..5168c76cff5336929be23a2f52179be287976b68 100644 --- a/drivers/staging/imx-drm/imx-ldb.c +++ b/drivers/staging/imx-drm/imx-ldb.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -58,9 +59,8 @@ struct imx_ldb; struct imx_ldb_channel { struct imx_ldb *ldb; struct drm_connector connector; - struct imx_drm_connector *imx_drm_connector; struct drm_encoder encoder; - struct imx_drm_encoder *imx_drm_encoder; + struct device_node *child; int chno; void *edid; int edid_len; @@ -91,11 +91,6 @@ static enum drm_connector_status imx_ldb_connector_detect( 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) { 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) 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( struct drm_connector *connector) { @@ -179,8 +168,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) u32 pixel_fmt; unsigned long serial_clk; unsigned long di_clk = mode->clock * 1000; - int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder, - encoder->crtc); + int mux = imx_drm_encoder_get_mux_id(encoder); if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { /* dual channel LVDS mode */ @@ -207,8 +195,7 @@ static void imx_ldb_encoder_prepare(struct drm_encoder *encoder) pixel_fmt = V4L2_PIX_FMT_RGB24; } - imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_LVDS, - pixel_fmt); + imx_drm_panel_format(encoder, pixel_fmt); } 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 *ldb = imx_ldb_ch->ldb; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; - int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->imx_drm_encoder, - encoder->crtc); + int mux = imx_drm_encoder_get_mux_id(encoder); if (dual) { clk_prepare_enable(ldb->clk[0]); @@ -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 = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .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 = { .get_modes = imx_ldb_connector_get_modes, .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 = { - .destroy = imx_ldb_encoder_destroy, + .destroy = imx_drm_encoder_destroy, }; 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) 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; + 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); if (ret) return ret; + if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) { - ret |= imx_ldb_get_clk(ldb, 1); + ret = imx_ldb_get_clk(ldb, 1); if (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, &imx_ldb_encoder_helper_funcs); - ret = imx_drm_add_encoder(&imx_ldb_ch->encoder, - &imx_ldb_ch->imx_drm_encoder, THIS_MODULE); - if (ret) { - dev_err(ldb->dev, "adding encoder failed with %d\n", ret); - return ret; - } + drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs, + DRM_MODE_ENCODER_LVDS); drm_connector_helper_add(&imx_ldb_ch->connector, &imx_ldb_connector_helper_funcs); - - ret = imx_drm_add_connector(&imx_ldb_ch->connector, - &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_connector_init(drm, &imx_ldb_ch->connector, + &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, &imx_ldb_ch->encoder); @@ -459,11 +431,12 @@ static const struct of_device_id 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 = - of_match_device(imx_ldb_dt_ids, &pdev->dev); + of_match_device(imx_ldb_dt_ids, dev); struct device_node *child; const u8 *edidp; struct imx_ldb *imx_ldb; @@ -473,17 +446,17 @@ static int imx_ldb_probe(struct platform_device *pdev) int ret; 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) return -ENOMEM; imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr"); 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); } - imx_ldb->dev = &pdev->dev; + imx_ldb->dev = dev; if (of_id) imx_ldb->lvds_mux = of_id->data; @@ -521,7 +494,7 @@ static int imx_ldb_probe(struct platform_device *pdev) return -EINVAL; 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; } @@ -531,6 +504,7 @@ static int imx_ldb_probe(struct platform_device *pdev) channel = &imx_ldb->channel[i]; channel->ldb = imx_ldb; channel->chno = i; + channel->child = child; edidp = of_get_property(child, "edid", &channel->edid_len); if (edidp) { @@ -560,7 +534,7 @@ static int imx_ldb_probe(struct platform_device *pdev) break; case LVDS_BIT_MAP_JEIDA: 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; } if (i == 0 || dual) @@ -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; break; default: - dev_err(&pdev->dev, "data mapping not specified or invalid\n"); + dev_err(dev, "data mapping not specified or invalid\n"); return -EINVAL; } - ret = imx_ldb_register(channel); + ret = imx_ldb_register(drm, channel); if (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; } -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; for (i = 0; i < 2; 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); - imx_drm_remove_encoder(channel->imx_drm_encoder); + channel->connector.funcs->destroy(&channel->connector); + 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; } diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index 9abc7ca8b6cf78ad8dba66329352c341a7c369d7..702c0c3204ed6093849813e810b551f105e6e1f6 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -110,9 +111,7 @@ enum { struct imx_tve { struct drm_connector connector; - struct imx_drm_connector *imx_drm_connector; struct drm_encoder encoder; - struct imx_drm_encoder *imx_drm_encoder; struct device *dev; spinlock_t lock; /* register lock */ bool enabled; @@ -225,11 +224,6 @@ static enum drm_connector_status imx_tve_connector_detect( 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) { struct imx_tve *tve = con_to_tve(connector); @@ -254,6 +248,11 @@ static int imx_tve_connector_mode_valid(struct drm_connector *connector, { struct imx_tve *tve = con_to_tve(connector); unsigned long rate; + int ret; + + ret = imx_drm_connector_mode_valid(connector, mode); + if (ret != MODE_OK) + return ret; /* pixel clock with 2x oversampling */ rate = clk_round_rate(tve->clk, 2000UL * mode->clock) / 2000; @@ -305,13 +304,11 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) switch (tve->mode) { case TVE_MODE_VGA: - imx_drm_crtc_panel_format_pins(encoder->crtc, - DRM_MODE_ENCODER_DAC, IPU_PIX_FMT_GBR24, + imx_drm_panel_format_pins(encoder, IPU_PIX_FMT_GBR24, tve->hsync_pin, tve->vsync_pin); break; case TVE_MODE_TVOUT: - imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_TVDAC, - V4L2_PIX_FMT_YUV444); + imx_drm_panel_format(encoder, V4L2_PIX_FMT_YUV444); break; } } @@ -364,16 +361,11 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder) 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 = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .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 = { @@ -383,7 +375,7 @@ static struct drm_connector_helper_funcs imx_tve_connector_helper_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 = { @@ -503,34 +495,27 @@ static int tve_clk_init(struct imx_tve *tve, void __iomem *base) 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; - tve->connector.funcs = &imx_tve_connector_funcs; - tve->encoder.funcs = &imx_tve_encoder_funcs; + encoder_type = tve->mode == TVE_MODE_VGA ? + DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; - tve->encoder.encoder_type = DRM_MODE_ENCODER_NONE; - tve->connector.connector_type = DRM_MODE_CONNECTOR_VGA; + ret = imx_drm_encoder_parse_of(drm, &tve->encoder, + tve->dev->of_node); + if (ret) + return ret; drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs); - ret = imx_drm_add_encoder(&tve->encoder, &tve->imx_drm_encoder, - THIS_MODULE); - if (ret) { - dev_err(tve->dev, "adding encoder failed with %d\n", ret); - return ret; - } + drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs, + encoder_type); drm_connector_helper_add(&tve->connector, &imx_tve_connector_helper_funcs); - - ret = imx_drm_add_connector(&tve->connector, - &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_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, + DRM_MODE_CONNECTOR_VGA); drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder); @@ -576,9 +561,11 @@ static const int of_get_tve_mode(struct device_node *np) 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 imx_tve *tve; struct resource *res; @@ -587,11 +574,11 @@ static int imx_tve_probe(struct platform_device *pdev) int irq; int ret; - tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL); + tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL); if (!tve) return -ENOMEM; - tve->dev = &pdev->dev; + tve->dev = dev; spin_lock_init(&tve->lock); ddc_node = of_parse_phandle(np, "ddc", 0); @@ -602,7 +589,7 @@ static int imx_tve_probe(struct platform_device *pdev) tve->mode = of_get_tve_mode(np); 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; } @@ -611,7 +598,7 @@ static int imx_tve_probe(struct platform_device *pdev) &tve->hsync_pin); if (ret < 0) { - dev_err(&pdev->dev, "failed to get vsync pin\n"); + dev_err(dev, "failed to get vsync pin\n"); return ret; } @@ -619,40 +606,40 @@ static int imx_tve_probe(struct platform_device *pdev) &tve->vsync_pin); if (ret < 0) { - dev_err(&pdev->dev, "failed to get vsync pin\n"); + dev_err(dev, "failed to get vsync pin\n"); return ret; } } 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)) return PTR_ERR(base); 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); 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)); return PTR_ERR(tve->regmap); } irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get irq\n"); + dev_err(dev, "failed to get irq\n"); 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", tve); 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; } - tve->dac_reg = devm_regulator_get(&pdev->dev, "dac"); + tve->dac_reg = devm_regulator_get(dev, "dac"); if (!IS_ERR(tve->dac_reg)) { regulator_set_voltage(tve->dac_reg, 2750000, 2750000); ret = regulator_enable(tve->dac_reg); @@ -660,17 +647,17 @@ static int imx_tve_probe(struct platform_device *pdev) return ret; } - tve->clk = devm_clk_get(&pdev->dev, "tve"); + tve->clk = devm_clk_get(dev, "tve"); 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)); return PTR_ERR(tve->clk); } /* 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)) { - 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)); return PTR_ERR(tve->di_sel_clk); } @@ -681,42 +668,51 @@ static int imx_tve_probe(struct platform_device *pdev) ret = regmap_read(tve->regmap, TVE_COM_CONF_REG, &val); 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; } 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; } /* disable cable detection for VGA mode */ ret = regmap_write(tve->regmap, TVE_CD_CONT_REG, 0); - ret = imx_tve_register(tve); + ret = imx_tve_register(drm, tve); if (ret) return ret; - ret = imx_drm_encoder_add_possible_crtcs(tve->imx_drm_encoder, np); - - platform_set_drvdata(pdev, tve); + dev_set_drvdata(dev, tve); 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 drm_connector *connector = &tve->connector; - struct drm_encoder *encoder = &tve->encoder; + struct imx_tve *tve = dev_get_drvdata(dev); - drm_mode_connector_detach_encoder(connector, encoder); - - imx_drm_remove_connector(tve->imx_drm_connector); - imx_drm_remove_encoder(tve->imx_drm_encoder); + tve->connector.funcs->destroy(&tve->connector); + tve->encoder.funcs->destroy(&tve->encoder); if (!IS_ERR(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; } diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c index 948a49b289ef171036d3f39e8ac0eb6392e59b3e..82a9ebad697c76b13181d809fa5c53c6d5200f62 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-di.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c @@ -19,9 +19,6 @@ #include #include #include -#include -#include -#include #include "imx-ipu-v3.h" #include "ipu-prv.h" @@ -33,10 +30,7 @@ struct ipu_di { struct clk *clk_di; /* display input clock */ struct clk *clk_ipu; /* IPU bus clock */ struct clk *clk_di_pixel; /* resulting pixel clock */ - struct clk_hw clk_hw_out; - char *clk_name; bool inuse; - unsigned long clkflags; struct ipu_soc *ipu; }; @@ -141,130 +135,6 @@ static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned 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, int wave_gen, int access_size, int component_size) @@ -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)); } +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) { u32 reg; u32 di_gen, vsync_cnt; u32 div; 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", 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) if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0)) 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 + sig->h_end_width; v_total = sig->height + sig->v_sync_width + sig->v_start_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); + ipu_di_config_clock(di, sig); + div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff; div = div / 16; /* Now divider is integer portion */ @@ -654,7 +621,11 @@ EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel); 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) return ret; @@ -666,6 +637,8 @@ EXPORT_SYMBOL_GPL(ipu_di_enable); int ipu_di_disable(struct ipu_di *di) { + WARN_ON(IS_ERR(di->clk_di_pixel)); + ipu_module_disable(di->ipu, di->module); clk_disable_unprepare(di->clk_di_pixel); @@ -721,13 +694,6 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, u32 module, struct clk *clk_ipu) { 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) return -ENODEV; @@ -749,45 +715,16 @@ int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id, if (!di->base) 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); - 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", id, base, di->base); di->inuse = false; di->ipu = ipu; return 0; - -failed_clk_register: - - kfree(di->clk_name); - - return ret; } 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); } diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c index 22be104fbda97526980481011ad5250121b52948..e646017c32ac573aa3d96bd5de949c2e9677cfc7 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 #include #include #include @@ -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_DI_CLKMODE_EXT; break; + case DRM_MODE_ENCODER_TMDS: case DRM_MODE_ENCODER_NONE: ipu_crtc->di_clkflags = 0; break; @@ -334,7 +336,7 @@ static int ipu_get_resources(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); int dp = -EINVAL; @@ -348,9 +350,9 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, 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_helper_funcs, THIS_MODULE, + &ipu_crtc_helper_funcs, ipu_crtc->dev->parent->of_node, pdata->di); if (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, 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; int ret; - if (!pdata) - 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); + 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) +{ + 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; } diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c index 351d61dede0024003f39c7ace312e552081c79ad..d610f0726bb291f03fa69772af23f5e3f8612615 100644 --- a/drivers/staging/imx-drm/parallel-display.c +++ b/drivers/staging/imx-drm/parallel-display.c @@ -18,6 +18,7 @@ * MA 02110-1301, USA. */ +#include #include #include #include @@ -32,9 +33,7 @@ struct imx_parallel_display { struct drm_connector connector; - struct imx_drm_connector *imx_drm_connector; struct drm_encoder encoder; - struct imx_drm_encoder *imx_drm_encoder; struct device *dev; void *edid; int edid_len; @@ -49,11 +48,6 @@ static enum drm_connector_status imx_pd_connector_detect( 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) { struct imx_parallel_display *imxpd = con_to_imxpd(connector); @@ -85,12 +79,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) 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( struct drm_connector *connector) { @@ -114,8 +102,7 @@ static void imx_pd_encoder_prepare(struct drm_encoder *encoder) { struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); - imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE, - imxpd->interface_pix_fmt); + imx_drm_panel_format(encoder, imxpd->interface_pix_fmt); } static void imx_pd_encoder_commit(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 = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .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 = { .get_modes = imx_pd_connector_get_modes, .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 = { - .destroy = imx_pd_encoder_destroy, + .destroy = imx_drm_encoder_destroy, }; 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, }; -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; - drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); - - imxpd->connector.funcs = &imx_pd_connector_funcs; - imxpd->encoder.funcs = &imx_pd_encoder_funcs; - - imxpd->encoder.encoder_type = DRM_MODE_ENCODER_NONE; - imxpd->connector.connector_type = DRM_MODE_CONNECTOR_VGA; + ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder, + imxpd->dev->of_node); + if (ret) + return ret; drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs); - ret = imx_drm_add_encoder(&imxpd->encoder, &imxpd->imx_drm_encoder, - THIS_MODULE); - if (ret) { - dev_err(imxpd->dev, "adding encoder failed with %d\n", ret); - return ret; - } + drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs, + DRM_MODE_ENCODER_NONE); drm_connector_helper_add(&imxpd->connector, &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, - &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; - } + drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder); imxpd->connector.encoder = &imxpd->encoder; 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; struct imx_parallel_display *imxpd; int ret; const char *fmt; - imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL); + imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL); if (!imxpd) return -ENOMEM; @@ -225,30 +198,39 @@ static int imx_pd_probe(struct platform_device *pdev) 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) return ret; - ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np); - - platform_set_drvdata(pdev, imxpd); + dev_set_drvdata(dev, imxpd); 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 drm_connector *connector = &imxpd->connector; - struct drm_encoder *encoder = &imxpd->encoder; + struct imx_parallel_display *imxpd = dev_get_drvdata(dev); - 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); - imx_drm_remove_encoder(imxpd->imx_drm_encoder); +static const struct component_ops imx_pd_ops = { + .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; }