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

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

Russell writes:

These changes, which convert imx-drm to use the recently merged
component infrastructure, have been reviewed and acked by Philipp Zabel,
Shawn Guo and Fabio Estevam, and are now deemed to be ready.
parents b4206006 d94905e0
......@@ -21,7 +21,7 @@ memory {
reg = <0x90000000 0x20000000>;
};
display@di0 {
display0: display@di0 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>;
interface-pix-fmt = "rgb24";
......@@ -43,7 +43,7 @@ timing0: dvi {
};
};
display@di1 {
display1: display@di1 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>;
interface-pix-fmt = "rgb565";
......@@ -81,6 +81,12 @@ power {
};
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>, <&ipu 1>;
connectors = <&display0>, <&display1>;
};
sound {
compatible = "fsl,imx51-babbage-sgtl5000",
"fsl,imx-audio-sgtl5000";
......
......@@ -21,7 +21,7 @@ memory {
};
soc {
display@di1 {
display1: display@di1 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>;
interface-pix-fmt = "bgr666";
......@@ -53,6 +53,12 @@ backlight {
default-brightness-level = <6>;
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&display1>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
......
......@@ -43,6 +43,12 @@ disp1: display@disp1 {
status = "disabled";
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&disp1>, <&tve>;
};
reg_3p2v: 3p2v {
compatible = "regulator-fixed";
regulator-name = "3P2V";
......
......@@ -21,7 +21,7 @@ memory {
reg = <0x70000000 0x40000000>;
};
display@di0 {
display0: display@di0 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>;
interface-pix-fmt = "rgb565";
......@@ -72,6 +72,12 @@ volume-down {
};
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>;
connectors = <&display0>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
......
......@@ -88,3 +88,8 @@ lvds-channel@1 {
crtcs = <&ipu1 0>, <&ipu1 1>;
};
};
&hdmi {
compatible = "fsl,imx6dl-hdmi";
crtcs = <&ipu1 0>, <&ipu1 1>;
};
......@@ -20,6 +20,10 @@ / {
compatible = "fsl,imx6q-sabresd", "fsl,imx6q";
};
&imx_drm {
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
};
&sata {
status = "okay";
};
......@@ -159,3 +159,8 @@ lvds-channel@1 {
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
};
};
&hdmi {
compatible = "fsl,imx6q-hdmi";
crtcs = <&ipu1 0>, <&ipu1 1>, <&ipu2 0>, <&ipu2 1>;
};
......@@ -62,6 +62,12 @@ volume-down {
};
};
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";
......
......@@ -1368,6 +1368,15 @@ lvds-channel@1 {
};
};
hdmi: hdmi@0120000 {
reg = <0x00120000 0x9000>;
interrupts = <0 115 0x04>;
gpr = <&gpr>;
clocks = <&clks 123>, <&clks 124>;
clock-names = "iahb", "isfr";
status = "disabled";
};
dcic1: dcic@020e4000 {
reg = <0x020e4000 0x4000>;
interrupts = <0 124 0x04>;
......
imxdrm-objs := imx-drm-core.o imx-fb.o
imxdrm-objs := imx-drm-core.o
obj-$(CONFIG_DRM_IMX) += imxdrm.o
obj-$(CONFIG_DRM_IMX_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
......
......@@ -13,14 +13,14 @@
* GNU General Public License for more details.
*
*/
#include <linux/component.h>
#include <linux/device.h>
#include <linux/fb.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include <linux/fb.h>
#include <linux/module.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
......@@ -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 <s.hauer@pengutronix.de>");
MODULE_DESCRIPTION("i.MX drm driver core");
......
......@@ -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_ */
/*
* i.MX drm driver
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
*
* Based on Samsung Exynos code
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include "imx-drm.h"
static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
};
void imx_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 64;
dev->mode_config.min_height = 64;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &imx_drm_mode_config_funcs;
}
/*
* i.MX drm driver
*
* Copyright (C) 2012 Sascha Hauer, Pengutronix
*
* Based on Samsung Exynos code
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include "imx-drm.h"
#define MAX_CONNECTOR 4
#define PREFERRED_BPP 16
static struct drm_fbdev_cma *fbdev_cma;
static int legacyfb_depth = 16;
module_param(legacyfb_depth, int, 0444);
static int __init imx_fb_helper_init(void)
{
struct drm_device *drm = imx_drm_device_get();
if (!drm)
return -EINVAL;
if (legacyfb_depth != 16 && legacyfb_depth != 32) {
pr_warn("i.MX legacyfb: invalid legacyfb_depth setting. defaulting to 16bpp\n");
legacyfb_depth = 16;
}
fbdev_cma = drm_fbdev_cma_init(drm, legacyfb_depth,
drm->mode_config.num_crtc, MAX_CONNECTOR);
if (IS_ERR(fbdev_cma)) {
imx_drm_device_put();
return PTR_ERR(fbdev_cma);
}
imx_drm_fb_helper_set(fbdev_cma);
return 0;
}
static void __exit imx_fb_helper_exit(void)
{
imx_drm_fb_helper_set(NULL);
drm_fbdev_cma_fini(fbdev_cma);
imx_drm_device_put();
}
late_initcall(imx_fb_helper_init);
module_exit(imx_fb_helper_exit);
MODULE_DESCRIPTION("Freescale i.MX legacy fb driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
......@@ -12,6 +12,7 @@
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*/
#include <linux/component.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/err.h>
......@@ -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;
}
......
......@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
......@@ -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;
}
......
......@@ -20,6 +20,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/component.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
......@@ -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;
}
......
......@@ -19,9 +19,6 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include "imx-ipu-v3.h"
#include "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);
}
......@@ -17,6 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <linux/component.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/device.h>
......@@ -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;
}
......
......@@ -18,6 +18,7 @@
* MA 02110-1301, USA.
*/
#include <linux/component.h>
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment