提交 58e75731 编写于 作者: G 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.
......@@ -21,7 +21,7 @@
reg = <0x90000000 0x20000000>;
};
display@di0 {
display0: display@di0 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>;
interface-pix-fmt = "rgb24";
......@@ -43,7 +43,7 @@
};
};
display@di1 {
display1: display@di1 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>;
interface-pix-fmt = "rgb565";
......@@ -81,6 +81,12 @@
};
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>, <&ipu 1>;
connectors = <&display0>, <&display1>;
};
sound {
compatible = "fsl,imx51-babbage-sgtl5000",
"fsl,imx-audio-sgtl5000";
......
......@@ -21,7 +21,7 @@
};
soc {
display@di1 {
display1: display@di1 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 1>;
interface-pix-fmt = "bgr666";
......@@ -53,6 +53,12 @@
default-brightness-level = <6>;
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&display1>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
......
......@@ -43,6 +43,12 @@
status = "disabled";
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 1>;
connectors = <&disp1>, <&tve>;
};
reg_3p2v: 3p2v {
compatible = "regulator-fixed";
regulator-name = "3P2V";
......
......@@ -21,7 +21,7 @@
reg = <0x70000000 0x40000000>;
};
display@di0 {
display0: display@di0 {
compatible = "fsl,imx-parallel-display";
crtcs = <&ipu 0>;
interface-pix-fmt = "rgb565";
......@@ -72,6 +72,12 @@
};
};
imx-drm {
compatible = "fsl,imx-drm";
crtcs = <&ipu 0>;
connectors = <&display0>;
};
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
......
......@@ -88,3 +88,8 @@
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 @@
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 @@
};
};
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 @@
};
};
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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册