提交 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
......
......@@ -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");
此差异已折叠。
......@@ -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.
先完成此消息的编辑!
想要评论请 注册