diff options
Diffstat (limited to 'drivers/video')
29 files changed, 1776 insertions, 1559 deletions
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index eb976ee..ea43724 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3624,7 +3624,7 @@ static int __init fb_console_init(void) return 0; } -module_init(fb_console_init); +fs_initcall(fb_console_init); #ifdef MODULE diff --git a/drivers/video/fbdev/omap2/displays-new/connector-hdmi.c b/drivers/video/fbdev/omap2/displays-new/connector-hdmi.c index 7b25967..219f14f 100644 --- a/drivers/video/fbdev/omap2/displays-new/connector-hdmi.c +++ b/drivers/video/fbdev/omap2/displays-new/connector-hdmi.c @@ -170,98 +170,6 @@ static bool hdmic_detect(struct omap_dss_device *dssdev) return in->ops.hdmi->detect(in); } -static int hdmic_audio_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - /* enable audio only if the display is active */ - if (!omapdss_device_is_enabled(dssdev)) - return -EPERM; - - r = in->ops.hdmi->audio_enable(in); - if (r) - return r; - - dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; - - return 0; -} - -static void hdmic_audio_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - in->ops.hdmi->audio_disable(in); - - dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; -} - -static int hdmic_audio_start(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - /* - * No need to check the panel state. It was checked when trasitioning - * to AUDIO_ENABLED. - */ - if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) - return -EPERM; - - r = in->ops.hdmi->audio_start(in); - if (r) - return r; - - dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; - - return 0; -} - -static void hdmic_audio_stop(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - in->ops.hdmi->audio_stop(in); - - dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; -} - -static bool hdmic_audio_supported(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - if (!omapdss_device_is_enabled(dssdev)) - return false; - - return in->ops.hdmi->audio_supported(in); -} - -static int hdmic_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - int r; - - /* config audio only if the display is active */ - if (!omapdss_device_is_enabled(dssdev)) - return -EPERM; - - r = in->ops.hdmi->audio_config(in, audio); - if (r) - return r; - - dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; - - return 0; -} - static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode) { struct panel_drv_data *ddata = to_panel_data(dssdev); @@ -296,13 +204,6 @@ static struct omap_dss_driver hdmic_driver = { .detect = hdmic_detect, .set_hdmi_mode = hdmic_set_hdmi_mode, .set_hdmi_infoframe = hdmic_set_infoframe, - - .audio_enable = hdmic_audio_enable, - .audio_disable = hdmic_audio_disable, - .audio_start = hdmic_audio_start, - .audio_stop = hdmic_audio_stop, - .audio_supported = hdmic_audio_supported, - .audio_config = hdmic_audio_config, }; static int hdmic_probe_pdata(struct platform_device *pdev) diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-tfp410.c b/drivers/video/fbdev/omap2/displays-new/encoder-tfp410.c index 47ee7cd..e349064 100644 --- a/drivers/video/fbdev/omap2/displays-new/encoder-tfp410.c +++ b/drivers/video/fbdev/omap2/displays-new/encoder-tfp410.c @@ -249,6 +249,7 @@ static int tfp410_probe(struct platform_device *pdev) dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; dssdev->owner = THIS_MODULE; dssdev->phy.dpi.data_lines = ddata->data_lines; + dssdev->port_num = 1; r = omapdss_register_output(dssdev); if (r) { diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c index c4abd56..c7a3ce2 100644 --- a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c +++ b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c @@ -193,55 +193,6 @@ static bool tpd_detect(struct omap_dss_device *dssdev) return gpio_get_value_cansleep(ddata->hpd_gpio); } -static int tpd_audio_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->audio_enable(in); -} - -static void tpd_audio_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - in->ops.hdmi->audio_disable(in); -} - -static int tpd_audio_start(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->audio_start(in); -} - -static void tpd_audio_stop(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - in->ops.hdmi->audio_stop(in); -} - -static bool tpd_audio_supported(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->audio_supported(in); -} - -static int tpd_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - struct omap_dss_device *in = ddata->in; - - return in->ops.hdmi->audio_config(in, audio); -} - static int tpd_set_infoframe(struct omap_dss_device *dssdev, const struct hdmi_avi_infoframe *avi) { @@ -275,13 +226,6 @@ static const struct omapdss_hdmi_ops tpd_hdmi_ops = { .detect = tpd_detect, .set_infoframe = tpd_set_infoframe, .set_hdmi_mode = tpd_set_hdmi_mode, - - .audio_enable = tpd_audio_enable, - .audio_disable = tpd_audio_disable, - .audio_start = tpd_audio_start, - .audio_stop = tpd_audio_stop, - .audio_supported = tpd_audio_supported, - .audio_config = tpd_audio_config, }; static int tpd_probe_pdata(struct platform_device *pdev) @@ -409,6 +353,7 @@ static int tpd_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_HDMI; dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; dssdev->owner = THIS_MODULE; + dssdev->port_num = 1; in = ddata->in; diff --git a/drivers/video/fbdev/omap2/dss/Kconfig b/drivers/video/fbdev/omap2/dss/Kconfig index 3d5eb6c..d1fa730 100644 --- a/drivers/video/fbdev/omap2/dss/Kconfig +++ b/drivers/video/fbdev/omap2/dss/Kconfig @@ -74,9 +74,6 @@ config OMAP4_DSS_HDMI help HDMI support for OMAP4 based SoCs. -config OMAP4_DSS_HDMI_AUDIO - bool - config OMAP5_DSS_HDMI bool "HDMI support for OMAP5" default n @@ -86,10 +83,6 @@ config OMAP5_DSS_HDMI Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI specification. -config OMAP5_DSS_HDMI_AUDIO - depends on OMAP5_DSS_HDMI - bool - config OMAP2_DSS_SDI bool "SDI support" default n diff --git a/drivers/video/fbdev/omap2/dss/Makefile b/drivers/video/fbdev/omap2/dss/Makefile index 245f933..2ea9d38 100644 --- a/drivers/video/fbdev/omap2/dss/Makefile +++ b/drivers/video/fbdev/omap2/dss/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o obj-$(CONFIG_OMAP2_DSS) += omapdss.o # Core DSS files omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \ - output.o dss-of.o + output.o dss-of.o pll.o # DSS compat layer files omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \ dispc-compat.o display-sysfs.o diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index 0e9a74b..0729c08 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -3028,7 +3028,7 @@ static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div, unsigned long dispc_fclk_rate(void) { - struct platform_device *dsidev; + struct dss_pll *pll; unsigned long r = 0; switch (dss_get_dispc_clk_source()) { @@ -3036,12 +3036,12 @@ unsigned long dispc_fclk_rate(void) r = dss_get_dispc_clk_rate(); break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: - dsidev = dsi_get_dsidev_from_id(0); - r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + pll = dss_pll_find("dsi0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: - dsidev = dsi_get_dsidev_from_id(1); - r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + pll = dss_pll_find("dsi1"); + r = pll->cinfo.clkout[0]; break; default: BUG(); @@ -3053,7 +3053,7 @@ unsigned long dispc_fclk_rate(void) unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) { - struct platform_device *dsidev; + struct dss_pll *pll; int lcd; unsigned long r; u32 l; @@ -3068,12 +3068,12 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) r = dss_get_dispc_clk_rate(); break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: - dsidev = dsi_get_dsidev_from_id(0); - r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + pll = dss_pll_find("dsi0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: - dsidev = dsi_get_dsidev_from_id(1); - r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + pll = dss_pll_find("dsi1"); + r = pll->cinfo.clkout[0]; break; default: BUG(); diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c index 4a3363d..2edf5ca 100644 --- a/drivers/video/fbdev/omap2/dss/dpi.c +++ b/drivers/video/fbdev/omap2/dss/dpi.c @@ -31,17 +31,20 @@ #include <linux/regulator/consumer.h> #include <linux/string.h> #include <linux/of.h> +#include <linux/clk.h> #include <video/omapdss.h> #include "dss.h" #include "dss_features.h" -static struct { +#define HSDIV_DISPC 0 + +struct dpi_data { struct platform_device *pdev; struct regulator *vdds_dsi_reg; - struct platform_device *dsidev; + struct dss_pll *pll; struct mutex lock; @@ -52,9 +55,20 @@ static struct { struct omap_dss_device output; bool port_initialized; -} dpi; +}; + +static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) +{ + return container_of(dssdev, struct dpi_data, output); +} + +/* only used in non-DT mode */ +static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev) +{ + return dev_get_drvdata(&pdev->dev); +} -static struct platform_device *dpi_get_dsidev(enum omap_channel channel) +static struct dss_pll *dpi_get_pll(enum omap_channel channel) { /* * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL @@ -75,9 +89,9 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel) case OMAPDSS_VER_OMAP4: switch (channel) { case OMAP_DSS_CHANNEL_LCD: - return dsi_get_dsidev_from_id(0); + return dss_pll_find("dsi0"); case OMAP_DSS_CHANNEL_LCD2: - return dsi_get_dsidev_from_id(1); + return dss_pll_find("dsi1"); default: return NULL; } @@ -85,9 +99,9 @@ static struct platform_device *dpi_get_dsidev(enum omap_channel channel) case OMAPDSS_VER_OMAP5: switch (channel) { case OMAP_DSS_CHANNEL_LCD: - return dsi_get_dsidev_from_id(0); + return dss_pll_find("dsi0"); case OMAP_DSS_CHANNEL_LCD3: - return dsi_get_dsidev_from_id(1); + return dss_pll_find("dsi1"); default: return NULL; } @@ -114,7 +128,7 @@ static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel) } struct dpi_clk_calc_ctx { - struct platform_device *dsidev; + struct dss_pll *pll; /* inputs */ @@ -122,7 +136,7 @@ struct dpi_clk_calc_ctx { /* outputs */ - struct dsi_clock_info dsi_cinfo; + struct dss_pll_clock_info dsi_cinfo; unsigned long fck; struct dispc_clock_info dispc_cinfo; }; @@ -154,7 +168,7 @@ static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck, } -static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, +static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc, void *data) { struct dpi_clk_calc_ctx *ctx = data; @@ -164,30 +178,31 @@ static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, * shifted. So skip all odd dividers when the pixel clock is on the * higher side. */ - if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 100000000) + if (m_dispc > 1 && m_dispc % 2 != 0 && ctx->pck_min >= 100000000) return false; - ctx->dsi_cinfo.regm_dispc = regm_dispc; - ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc; + ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; + ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max, dpi_calc_dispc_cb, ctx); } -static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint, - unsigned long pll, +static bool dpi_calc_pll_cb(int n, int m, unsigned long fint, + unsigned long clkdco, void *data) { struct dpi_clk_calc_ctx *ctx = data; - ctx->dsi_cinfo.regn = regn; - ctx->dsi_cinfo.regm = regm; + ctx->dsi_cinfo.n = n; + ctx->dsi_cinfo.m = m; ctx->dsi_cinfo.fint = fint; - ctx->dsi_cinfo.clkin4ddr = pll; + ctx->dsi_cinfo.clkdco = clkdco; - return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min, - dpi_calc_hsdiv_cb, ctx); + return dss_pll_hsdiv_calc(ctx->pll, clkdco, + ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), + dpi_calc_hsdiv_cb, ctx); } static bool dpi_calc_dss_cb(unsigned long fck, void *data) @@ -200,23 +215,23 @@ static bool dpi_calc_dss_cb(unsigned long fck, void *data) dpi_calc_dispc_cb, ctx); } -static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) +static bool dpi_dsi_clk_calc(struct dpi_data *dpi, unsigned long pck, + struct dpi_clk_calc_ctx *ctx) { unsigned long clkin; unsigned long pll_min, pll_max; - clkin = dsi_get_pll_clkin(dpi.dsidev); - memset(ctx, 0, sizeof(*ctx)); - ctx->dsidev = dpi.dsidev; + ctx->pll = dpi->pll; ctx->pck_min = pck - 1000; ctx->pck_max = pck + 1000; - ctx->dsi_cinfo.clkin = clkin; pll_min = 0; pll_max = 0; - return dsi_pll_calc(dpi.dsidev, clkin, + clkin = clk_get_rate(ctx->pll->clkin); + + return dss_pll_calc(ctx->pll, clkin, pll_min, pll_max, dpi_calc_pll_cb, ctx); } @@ -252,7 +267,7 @@ static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx) -static int dpi_set_dsi_clk(enum omap_channel channel, +static int dpi_set_dsi_clk(struct dpi_data *dpi, enum omap_channel channel, unsigned long pck_req, unsigned long *fck, int *lck_div, int *pck_div) { @@ -260,28 +275,28 @@ static int dpi_set_dsi_clk(enum omap_channel channel, int r; bool ok; - ok = dpi_dsi_clk_calc(pck_req, &ctx); + ok = dpi_dsi_clk_calc(dpi, pck_req, &ctx); if (!ok) return -EINVAL; - r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo); + r = dss_pll_set_config(dpi->pll, &ctx.dsi_cinfo); if (r) return r; dss_select_lcd_clk_source(channel, dpi_get_alt_clk_src(channel)); - dpi.mgr_config.clock_info = ctx.dispc_cinfo; + dpi->mgr_config.clock_info = ctx.dispc_cinfo; - *fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; + *fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC]; *lck_div = ctx.dispc_cinfo.lck_div; *pck_div = ctx.dispc_cinfo.pck_div; return 0; } -static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, - int *lck_div, int *pck_div) +static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req, + unsigned long *fck, int *lck_div, int *pck_div) { struct dpi_clk_calc_ctx ctx; int r; @@ -295,7 +310,7 @@ static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, if (r) return r; - dpi.mgr_config.clock_info = ctx.dispc_cinfo; + dpi->mgr_config.clock_info = ctx.dispc_cinfo; *fck = ctx.fck; *lck_div = ctx.dispc_cinfo.lck_div; @@ -304,19 +319,21 @@ static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck, return 0; } -static int dpi_set_mode(struct omap_overlay_manager *mgr) +static int dpi_set_mode(struct dpi_data *dpi) { - struct omap_video_timings *t = &dpi.timings; + struct omap_dss_device *out = &dpi->output; + struct omap_overlay_manager *mgr = out->manager; + struct omap_video_timings *t = &dpi->timings; int lck_div = 0, pck_div = 0; unsigned long fck = 0; unsigned long pck; int r = 0; - if (dpi.dsidev) - r = dpi_set_dsi_clk(mgr->id, t->pixelclock, &fck, + if (dpi->pll) + r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck, &lck_div, &pck_div); else - r = dpi_set_dispc_clk(t->pixelclock, &fck, + r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck, &lck_div, &pck_div); if (r) return r; @@ -335,28 +352,32 @@ static int dpi_set_mode(struct omap_overlay_manager *mgr) return 0; } -static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr) +static void dpi_config_lcd_manager(struct dpi_data *dpi) { - dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; + struct omap_dss_device *out = &dpi->output; + struct omap_overlay_manager *mgr = out->manager; + + dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; - dpi.mgr_config.stallmode = false; - dpi.mgr_config.fifohandcheck = false; + dpi->mgr_config.stallmode = false; + dpi->mgr_config.fifohandcheck = false; - dpi.mgr_config.video_port_width = dpi.data_lines; + dpi->mgr_config.video_port_width = dpi->data_lines; - dpi.mgr_config.lcden_sig_polarity = 0; + dpi->mgr_config.lcden_sig_polarity = 0; - dss_mgr_set_lcd_config(mgr, &dpi.mgr_config); + dss_mgr_set_lcd_config(mgr, &dpi->mgr_config); } static int dpi_display_enable(struct omap_dss_device *dssdev) { - struct omap_dss_device *out = &dpi.output; + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + struct omap_dss_device *out = &dpi->output; int r; - mutex_lock(&dpi.lock); + mutex_lock(&dpi->lock); - if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) { + if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) { DSSERR("no VDSS_DSI regulator\n"); r = -ENODEV; goto err_no_reg; @@ -369,7 +390,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) } if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) { - r = regulator_enable(dpi.vdds_dsi_reg); + r = regulator_enable(dpi->vdds_dsi_reg); if (r) goto err_reg_enable; } @@ -378,25 +399,21 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_get_dispc; - r = dss_dpi_select_source(out->manager->id); + r = dss_dpi_select_source(out->port_num, out->manager->id); if (r) goto err_src_sel; - if (dpi.dsidev) { - r = dsi_runtime_get(dpi.dsidev); - if (r) - goto err_get_dsi; - - r = dsi_pll_init(dpi.dsidev, 0, 1); + if (dpi->pll) { + r = dss_pll_enable(dpi->pll); if (r) goto err_dsi_pll_init; } - r = dpi_set_mode(out->manager); + r = dpi_set_mode(dpi); if (r) goto err_set_mode; - dpi_config_lcd_manager(out->manager); + dpi_config_lcd_manager(dpi); mdelay(2); @@ -404,78 +421,80 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_mgr_enable; - mutex_unlock(&dpi.lock); + mutex_unlock(&dpi->lock); return 0; err_mgr_enable: err_set_mode: - if (dpi.dsidev) - dsi_pll_uninit(dpi.dsidev, true); + if (dpi->pll) + dss_pll_disable(dpi->pll); err_dsi_pll_init: - if (dpi.dsidev) - dsi_runtime_put(dpi.dsidev); -err_get_dsi: err_src_sel: dispc_runtime_put(); err_get_dispc: if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) - regulator_disable(dpi.vdds_dsi_reg); + regulator_disable(dpi->vdds_dsi_reg); err_reg_enable: err_no_out_mgr: err_no_reg: - mutex_unlock(&dpi.lock); + mutex_unlock(&dpi->lock); return r; } static void dpi_display_disable(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = dpi.output.manager; + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + struct omap_overlay_manager *mgr = dpi->output.manager; - mutex_lock(&dpi.lock); + mutex_lock(&dpi->lock); dss_mgr_disable(mgr); - if (dpi.dsidev) { + if (dpi->pll) { dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); - dsi_pll_uninit(dpi.dsidev, true); - dsi_runtime_put(dpi.dsidev); + dss_pll_disable(dpi->pll); } dispc_runtime_put(); if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) - regulator_disable(dpi.vdds_dsi_reg); + regulator_disable(dpi->vdds_dsi_reg); - mutex_unlock(&dpi.lock); + mutex_unlock(&dpi->lock); } static void dpi_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + DSSDBG("dpi_set_timings\n"); - mutex_lock(&dpi.lock); + mutex_lock(&dpi->lock); - dpi.timings = *timings; + dpi->timings = *timings; - mutex_unlock(&dpi.lock); + mutex_unlock(&dpi->lock); } static void dpi_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - mutex_lock(&dpi.lock); + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + + mutex_lock(&dpi->lock); - *timings = dpi.timings; + *timings = dpi->timings; - mutex_unlock(&dpi.lock); + mutex_unlock(&dpi->lock); } static int dpi_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct omap_overlay_manager *mgr = dpi.output.manager; + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + struct omap_overlay_manager *mgr = dpi->output.manager; int lck_div, pck_div; unsigned long fck; unsigned long pck; @@ -488,12 +507,12 @@ static int dpi_check_timings(struct omap_dss_device *dssdev, if (timings->pixelclock == 0) return -EINVAL; - if (dpi.dsidev) { - ok = dpi_dsi_clk_calc(timings->pixelclock, &ctx); + if (dpi->pll) { + ok = dpi_dsi_clk_calc(dpi, timings->pixelclock, &ctx); if (!ok) return -EINVAL; - fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk; + fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC]; } else { ok = dpi_dss_clk_calc(timings->pixelclock, &ctx); if (!ok) @@ -514,74 +533,69 @@ static int dpi_check_timings(struct omap_dss_device *dssdev, static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines) { - mutex_lock(&dpi.lock); + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - dpi.data_lines = data_lines; + mutex_lock(&dpi->lock); - mutex_unlock(&dpi.lock); + dpi->data_lines = data_lines; + + mutex_unlock(&dpi->lock); } -static int dpi_verify_dsi_pll(struct platform_device *dsidev) +static int dpi_verify_dsi_pll(struct dss_pll *pll) { int r; /* do initial setup with the PLL to see if it is operational */ - r = dsi_runtime_get(dsidev); + r = dss_pll_enable(pll); if (r) return r; - r = dsi_pll_init(dsidev, 0, 1); - if (r) { - dsi_runtime_put(dsidev); - return r; - } - - dsi_pll_uninit(dsidev, true); - dsi_runtime_put(dsidev); + dss_pll_disable(pll); return 0; } -static int dpi_init_regulator(void) +static int dpi_init_regulator(struct dpi_data *dpi) { struct regulator *vdds_dsi; if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) return 0; - if (dpi.vdds_dsi_reg) + if (dpi->vdds_dsi_reg) return 0; - vdds_dsi = devm_regulator_get(&dpi.pdev->dev, "vdds_dsi"); + vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi"); if (IS_ERR(vdds_dsi)) { if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) DSSERR("can't get VDDS_DSI regulator\n"); return PTR_ERR(vdds_dsi); } - dpi.vdds_dsi_reg = vdds_dsi; + dpi->vdds_dsi_reg = vdds_dsi; return 0; } -static void dpi_init_pll(void) +static void dpi_init_pll(struct dpi_data *dpi) { - struct platform_device *dsidev; + struct dss_pll *pll; - if (dpi.dsidev) + if (dpi->pll) return; - dsidev = dpi_get_dsidev(dpi.output.dispc_channel); - if (!dsidev) + pll = dpi_get_pll(dpi->output.dispc_channel); + if (!pll) return; - if (dpi_verify_dsi_pll(dsidev)) { + if (dpi_verify_dsi_pll(pll)) { DSSWARN("DSI PLL not operational\n"); return; } - dpi.dsidev = dsidev; + dpi->pll = pll; } /* @@ -590,7 +604,7 @@ static void dpi_init_pll(void) * the channel in some more dynamic manner, or get the channel as a user * parameter. */ -static enum omap_channel dpi_get_channel(void) +static enum omap_channel dpi_get_channel(int port_num) { switch (omapdss_get_version()) { case OMAPDSS_VER_OMAP24xx: @@ -618,14 +632,15 @@ static enum omap_channel dpi_get_channel(void) static int dpi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); struct omap_overlay_manager *mgr; int r; - r = dpi_init_regulator(); + r = dpi_init_regulator(dpi); if (r) return r; - dpi_init_pll(); + dpi_init_pll(dpi); mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); if (!mgr) @@ -676,13 +691,14 @@ static const struct omapdss_dpi_ops dpi_ops = { static void dpi_init_output(struct platform_device *pdev) { - struct omap_dss_device *out = &dpi.output; + struct dpi_data *dpi = dpi_get_data_from_pdev(pdev); + struct omap_dss_device *out = &dpi->output; out->dev = &pdev->dev; out->id = OMAP_DSS_OUTPUT_DPI; out->output_type = OMAP_DISPLAY_TYPE_DPI; out->name = "dpi.0"; - out->dispc_channel = dpi_get_channel(); + out->dispc_channel = dpi_get_channel(0); out->ops.dpi = &dpi_ops; out->owner = THIS_MODULE; @@ -691,16 +707,69 @@ static void dpi_init_output(struct platform_device *pdev) static void __exit dpi_uninit_output(struct platform_device *pdev) { - struct omap_dss_device *out = &dpi.output; + struct dpi_data *dpi = dpi_get_data_from_pdev(pdev); + struct omap_dss_device *out = &dpi->output; + + omapdss_unregister_output(out); +} + +static void dpi_init_output_port(struct platform_device *pdev, + struct device_node *port) +{ + struct dpi_data *dpi = port->data; + struct omap_dss_device *out = &dpi->output; + int r; + u32 port_num; + + r = of_property_read_u32(port, "reg", &port_num); + if (r) + port_num = 0; + + switch (port_num) { + case 2: + out->name = "dpi.2"; + break; + case 1: + out->name = "dpi.1"; + break; + case 0: + default: + out->name = "dpi.0"; + break; + } + + out->dev = &pdev->dev; + out->id = OMAP_DSS_OUTPUT_DPI; + out->output_type = OMAP_DISPLAY_TYPE_DPI; + out->dispc_channel = dpi_get_channel(port_num); + out->port_num = port_num; + out->ops.dpi = &dpi_ops; + out->owner = THIS_MODULE; + + omapdss_register_output(out); +} + +static void __exit dpi_uninit_output_port(struct device_node *port) +{ + struct dpi_data *dpi = port->data; + struct omap_dss_device *out = &dpi->output; omapdss_unregister_output(out); } static int omap_dpi_probe(struct platform_device *pdev) { - dpi.pdev = pdev; + struct dpi_data *dpi; + + dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); + if (!dpi) + return -ENOMEM; - mutex_init(&dpi.lock); + dpi->pdev = pdev; + + dev_set_drvdata(&pdev->dev, dpi); + + mutex_init(&dpi->lock); dpi_init_output(pdev); @@ -736,10 +805,15 @@ void __exit dpi_uninit_platform_driver(void) int __init dpi_init_port(struct platform_device *pdev, struct device_node *port) { + struct dpi_data *dpi; struct device_node *ep; u32 datalines; int r; + dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); + if (!dpi) + return -ENOMEM; + ep = omapdss_of_get_next_endpoint(port, NULL); if (!ep) return 0; @@ -750,17 +824,18 @@ int __init dpi_init_port(struct platform_device *pdev, struct device_node *port) goto err_datalines; } - dpi.data_lines = datalines; + dpi->data_lines = datalines; of_node_put(ep); - dpi.pdev = pdev; + dpi->pdev = pdev; + port->data = dpi; - mutex_init(&dpi.lock); + mutex_init(&dpi->lock); - dpi_init_output(pdev); + dpi_init_output_port(pdev, port); - dpi.port_initialized = true; + dpi->port_initialized = true; return 0; @@ -770,10 +845,12 @@ err_datalines: return r; } -void __exit dpi_uninit_port(void) +void __exit dpi_uninit_port(struct device_node *port) { - if (!dpi.port_initialized) + struct dpi_data *dpi = port->data; + + if (!dpi->port_initialized) return; - dpi_uninit_output(dpi.pdev); + dpi_uninit_output_port(port); } diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c index 0793bc6..73af351 100644 --- a/drivers/video/fbdev/omap2/dss/dsi.c +++ b/drivers/video/fbdev/omap2/dss/dsi.c @@ -219,6 +219,10 @@ static void dsi_display_uninit_dispc(struct platform_device *dsidev, static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel); +/* DSI PLL HSDIV indices */ +#define HSDIV_DISPC 0 +#define HSDIV_DSI 1 + #define DSI_MAX_NR_ISRS 2 #define DSI_MAX_NR_LANES 5 @@ -271,6 +275,7 @@ struct dsi_isr_tables { struct dsi_clk_calc_ctx { struct platform_device *dsidev; + struct dss_pll *pll; /* inputs */ @@ -280,13 +285,18 @@ struct dsi_clk_calc_ctx { /* outputs */ - struct dsi_clock_info dsi_cinfo; + struct dss_pll_clock_info dsi_cinfo; struct dispc_clock_info dispc_cinfo; struct omap_video_timings dispc_vm; struct omap_dss_dsi_videomode_timings dsi_vm; }; +struct dsi_lp_clock_info { + unsigned long lp_clk; + u16 lp_clk_div; +}; + struct dsi_data { struct platform_device *pdev; void __iomem *proto_base; @@ -300,12 +310,14 @@ struct dsi_data { bool is_enabled; struct clk *dss_clk; - struct clk *sys_clk; struct dispc_clock_info user_dispc_cinfo; - struct dsi_clock_info user_dsi_cinfo; + struct dss_pll_clock_info user_dsi_cinfo; + + struct dsi_lp_clock_info user_lp_cinfo; + struct dsi_lp_clock_info current_lp_cinfo; - struct dsi_clock_info current_cinfo; + struct dss_pll pll; bool vdds_dsi_enabled; struct regulator *vdds_dsi_reg; @@ -321,8 +333,6 @@ struct dsi_data { struct mutex lock; struct semaphore bus_lock; - unsigned pll_locked; - spinlock_t irq_lock; struct dsi_isr_tables isr_tables; /* space for a copy used by the interrupt handler */ @@ -347,7 +357,7 @@ struct dsi_data { unsigned long cache_req_pck; unsigned long cache_clk_freq; - struct dsi_clock_info cache_cinfo; + struct dss_pll_clock_info cache_cinfo; u32 errors; spinlock_t errors_lock; @@ -362,11 +372,6 @@ struct dsi_data { spinlock_t irq_stats_lock; struct dsi_irq_stats irq_stats; #endif - /* DSI PLL Parameter Ranges */ - unsigned long regm_max, regn_max; - unsigned long regm_dispc_max, regm_dsi_max; - unsigned long fint_min, fint_max; - unsigned long lpdiv_max; unsigned num_lanes_supported; unsigned line_buffer_size; @@ -412,7 +417,7 @@ static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss return to_platform_device(dssdev->dev); } -struct platform_device *dsi_get_dsidev_from_id(int module) +static struct platform_device *dsi_get_dsidev_from_id(int module) { struct omap_dss_device *out; enum omap_dss_output_id id; @@ -1134,7 +1139,7 @@ static u32 dsi_get_errors(struct platform_device *dsidev) return e; } -int dsi_runtime_get(struct platform_device *dsidev) +static int dsi_runtime_get(struct platform_device *dsidev) { int r; struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); @@ -1146,7 +1151,7 @@ int dsi_runtime_get(struct platform_device *dsidev) return r < 0 ? r : 0; } -void dsi_runtime_put(struct platform_device *dsidev) +static void dsi_runtime_put(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; @@ -1188,23 +1193,6 @@ static int dsi_regulator_init(struct platform_device *dsidev) return 0; } -/* source clock for DSI PLL. this could also be PCLKFREE */ -static inline void dsi_enable_pll_clock(struct platform_device *dsidev, - bool enable) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (enable) - clk_prepare_enable(dsi->sys_clk); - else - clk_disable_unprepare(dsi->sys_clk); - - if (enable && dsi->pll_locked) { - if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) - DSSERR("cannot lock PLL when enabling clocks\n"); - } -} - static void _dsi_print_reset_status(struct platform_device *dsidev) { u32 l; @@ -1256,25 +1244,25 @@ static inline int dsi_if_enable(struct platform_device *dsidev, bool enable) return 0; } -unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) +static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk; + return dsi->pll.cinfo.clkout[HSDIV_DISPC]; } static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk; + return dsi->pll.cinfo.clkout[HSDIV_DSI]; } static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return dsi->current_cinfo.clkin4ddr / 16; + return dsi->pll.cinfo.clkdco / 16; } static unsigned long dsi_fclk_rate(struct platform_device *dsidev) @@ -1293,10 +1281,10 @@ static unsigned long dsi_fclk_rate(struct platform_device *dsidev) return r; } -static int dsi_lp_clock_calc(struct dsi_clock_info *cinfo, - unsigned long lp_clk_min, unsigned long lp_clk_max) +static int dsi_lp_clock_calc(unsigned long dsi_fclk, + unsigned long lp_clk_min, unsigned long lp_clk_max, + struct dsi_lp_clock_info *lp_cinfo) { - unsigned long dsi_fclk = cinfo->dsi_pll_hsdiv_dsi_clk; unsigned lp_clk_div; unsigned long lp_clk; @@ -1306,8 +1294,8 @@ static int dsi_lp_clock_calc(struct dsi_clock_info *cinfo, if (lp_clk < lp_clk_min || lp_clk > lp_clk_max) return -EINVAL; - cinfo->lp_clk_div = lp_clk_div; - cinfo->lp_clk = lp_clk; + lp_cinfo->lp_clk_div = lp_clk_div; + lp_cinfo->lp_clk = lp_clk; return 0; } @@ -1318,10 +1306,12 @@ static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) unsigned long dsi_fclk; unsigned lp_clk_div; unsigned long lp_clk; + unsigned lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); + - lp_clk_div = dsi->user_dsi_cinfo.lp_clk_div; + lp_clk_div = dsi->user_lp_cinfo.lp_clk_div; - if (lp_clk_div == 0 || lp_clk_div > dsi->lpdiv_max) + if (lp_clk_div == 0 || lp_clk_div > lpdiv_max) return -EINVAL; dsi_fclk = dsi_fclk_rate(dsidev); @@ -1329,8 +1319,8 @@ static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) lp_clk = dsi_fclk / 2 / lp_clk_div; DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); - dsi->current_cinfo.lp_clk = lp_clk; - dsi->current_cinfo.lp_clk_div = lp_clk_div; + dsi->current_lp_cinfo.lp_clk = lp_clk; + dsi->current_lp_cinfo.lp_clk_div = lp_clk_div; /* LP_CLK_DIVISOR */ REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0); @@ -1391,286 +1381,33 @@ static int dsi_pll_power(struct platform_device *dsidev, return 0; } -unsigned long dsi_get_pll_clkin(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - return clk_get_rate(dsi->sys_clk); -} - -bool dsi_hsdiv_calc(struct platform_device *dsidev, unsigned long pll, - unsigned long out_min, dsi_hsdiv_calc_func func, void *data) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int regm, regm_start, regm_stop; - unsigned long out_max; - unsigned long out; - - out_min = out_min ? out_min : 1; - out_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); - - regm_start = max(DIV_ROUND_UP(pll, out_max), 1ul); - regm_stop = min(pll / out_min, dsi->regm_dispc_max); - - for (regm = regm_start; regm <= regm_stop; ++regm) { - out = pll / regm; - - if (func(regm, out, data)) - return true; - } - - return false; -} - -bool dsi_pll_calc(struct platform_device *dsidev, unsigned long clkin, - unsigned long pll_min, unsigned long pll_max, - dsi_pll_calc_func func, void *data) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int regn, regn_start, regn_stop; - int regm, regm_start, regm_stop; - unsigned long fint, pll; - const unsigned long pll_hw_max = 1800000000; - unsigned long fint_hw_min, fint_hw_max; - - fint_hw_min = dsi->fint_min; - fint_hw_max = dsi->fint_max; - regn_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); - regn_stop = min(clkin / fint_hw_min, dsi->regn_max); - - pll_max = pll_max ? pll_max : ULONG_MAX; - - for (regn = regn_start; regn <= regn_stop; ++regn) { - fint = clkin / regn; - - regm_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), - 1ul); - regm_stop = min3(pll_max / fint / 2, - pll_hw_max / fint / 2, - dsi->regm_max); - - for (regm = regm_start; regm <= regm_stop; ++regm) { - pll = 2 * regm * fint; - - if (func(regn, regm, fint, pll, data)) - return true; - } - } - - return false; -} - -/* calculate clock rates using dividers in cinfo */ -static int dsi_calc_clock_rates(struct platform_device *dsidev, - struct dsi_clock_info *cinfo) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) - return -EINVAL; - - if (cinfo->regm == 0 || cinfo->regm > dsi->regm_max) - return -EINVAL; - - if (cinfo->regm_dispc > dsi->regm_dispc_max) - return -EINVAL; - - if (cinfo->regm_dsi > dsi->regm_dsi_max) - return -EINVAL; - - cinfo->clkin = clk_get_rate(dsi->sys_clk); - cinfo->fint = cinfo->clkin / cinfo->regn; - - if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) - return -EINVAL; - - cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint; - - if (cinfo->clkin4ddr > 1800 * 1000 * 1000) - return -EINVAL; - - if (cinfo->regm_dispc > 0) - cinfo->dsi_pll_hsdiv_dispc_clk = - cinfo->clkin4ddr / cinfo->regm_dispc; - else - cinfo->dsi_pll_hsdiv_dispc_clk = 0; - - if (cinfo->regm_dsi > 0) - cinfo->dsi_pll_hsdiv_dsi_clk = - cinfo->clkin4ddr / cinfo->regm_dsi; - else - cinfo->dsi_pll_hsdiv_dsi_clk = 0; - - return 0; -} - -static void dsi_pll_calc_dsi_fck(struct dsi_clock_info *cinfo) +static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo) { unsigned long max_dsi_fck; max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK); - cinfo->regm_dsi = DIV_ROUND_UP(cinfo->clkin4ddr, max_dsi_fck); - cinfo->dsi_pll_hsdiv_dsi_clk = cinfo->clkin4ddr / cinfo->regm_dsi; + cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck); + cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI]; } -int dsi_pll_set_clock_div(struct platform_device *dsidev, - struct dsi_clock_info *cinfo) +static int dsi_pll_enable(struct dss_pll *pll) { - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); + struct platform_device *dsidev = dsi->pdev; int r = 0; - u32 l; - int f = 0; - u8 regn_start, regn_end, regm_start, regm_end; - u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end; - - DSSDBG("DSI PLL clock config starts"); - - dsi->current_cinfo.clkin = cinfo->clkin; - dsi->current_cinfo.fint = cinfo->fint; - dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; - dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = - cinfo->dsi_pll_hsdiv_dispc_clk; - dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk = - cinfo->dsi_pll_hsdiv_dsi_clk; - - dsi->current_cinfo.regn = cinfo->regn; - dsi->current_cinfo.regm = cinfo->regm; - dsi->current_cinfo.regm_dispc = cinfo->regm_dispc; - dsi->current_cinfo.regm_dsi = cinfo->regm_dsi; - - DSSDBG("DSI Fint %ld\n", cinfo->fint); - - DSSDBG("clkin rate %ld\n", cinfo->clkin); - - /* DSIPHY == CLKIN4DDR */ - DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n", - cinfo->regm, - cinfo->regn, - cinfo->clkin, - cinfo->clkin4ddr); - - DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", - cinfo->clkin4ddr / 1000 / 1000 / 2); - - DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4); - - DSSDBG("regm_dispc = %d, %s (%s) = %lu\n", cinfo->regm_dispc, - dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), - dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), - cinfo->dsi_pll_hsdiv_dispc_clk); - DSSDBG("regm_dsi = %d, %s (%s) = %lu\n", cinfo->regm_dsi, - dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), - dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), - cinfo->dsi_pll_hsdiv_dsi_clk); - - dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGN, ®n_start, ®n_end); - dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM, ®m_start, ®m_end); - dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DISPC, ®m_dispc_start, - ®m_dispc_end); - dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DSI, ®m_dsi_start, - ®m_dsi_end); - - /* DSI_PLL_AUTOMODE = manual */ - REG_FLD_MOD(dsidev, DSI_PLL_CONTROL, 0, 0, 0); - - l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION1); - l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ - /* DSI_PLL_REGN */ - l = FLD_MOD(l, cinfo->regn - 1, regn_start, regn_end); - /* DSI_PLL_REGM */ - l = FLD_MOD(l, cinfo->regm, regm_start, regm_end); - /* DSI_CLOCK_DIV */ - l = FLD_MOD(l, cinfo->regm_dispc > 0 ? cinfo->regm_dispc - 1 : 0, - regm_dispc_start, regm_dispc_end); - /* DSIPROTO_CLOCK_DIV */ - l = FLD_MOD(l, cinfo->regm_dsi > 0 ? cinfo->regm_dsi - 1 : 0, - regm_dsi_start, regm_dsi_end); - dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION1, l); - - BUG_ON(cinfo->fint < dsi->fint_min || cinfo->fint > dsi->fint_max); - - l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); - - if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) { - f = cinfo->fint < 1000000 ? 0x3 : - cinfo->fint < 1250000 ? 0x4 : - cinfo->fint < 1500000 ? 0x5 : - cinfo->fint < 1750000 ? 0x6 : - 0x7; - - l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ - } else if (dss_has_feature(FEAT_DSI_PLL_SELFREQDCO)) { - f = cinfo->clkin4ddr < 1000000000 ? 0x2 : 0x4; - - l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */ - } - - l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ - l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ - l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ - if (dss_has_feature(FEAT_DSI_PLL_REFSEL)) - l = FLD_MOD(l, 3, 22, 21); /* REF_SYSCLK = sysclk */ - dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); - - REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ - - if (wait_for_bit_change(dsidev, DSI_PLL_GO, 0, 0) != 0) { - DSSERR("dsi pll go bit not going down.\n"); - r = -EIO; - goto err; - } - - if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) { - DSSERR("cannot lock PLL\n"); - r = -EIO; - goto err; - } - - dsi->pll_locked = 1; - - l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); - l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ - l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ - l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ - l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ - l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ - l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ - l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ - l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */ - l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */ - l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */ - l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */ - l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ - l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ - l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); - - DSSDBG("PLL config done\n"); -err: - return r; -} - -int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, - bool enable_hsdiv) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - int r = 0; - enum dsi_pll_power_state pwstate; DSSDBG("PLL init\n"); - /* - * It seems that on many OMAPs we need to enable both to have a - * functional HSDivider. - */ - enable_hsclk = enable_hsdiv = true; - r = dsi_regulator_init(dsidev); if (r) return r; - dsi_enable_pll_clock(dsidev, 1); + r = dsi_runtime_get(dsidev); + if (r) + return r; + /* * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. */ @@ -1697,16 +1434,7 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, * fill the whole display. No idea about this */ dispc_pck_free_enable(0); - if (enable_hsclk && enable_hsdiv) - pwstate = DSI_PLL_POWER_ON_ALL; - else if (enable_hsclk) - pwstate = DSI_PLL_POWER_ON_HSCLK; - else if (enable_hsdiv) - pwstate = DSI_PLL_POWER_ON_DIV; - else - pwstate = DSI_PLL_POWER_OFF; - - r = dsi_pll_power(dsidev, pwstate); + r = dsi_pll_power(dsidev, DSI_PLL_POWER_ON_ALL); if (r) goto err1; @@ -1721,15 +1449,14 @@ err1: } err0: dsi_disable_scp_clk(dsidev); - dsi_enable_pll_clock(dsidev, 0); + dsi_runtime_put(dsidev); return r; } -void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) +static void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - dsi->pll_locked = 0; dsi_pll_power(dsidev, DSI_PLL_POWER_OFF); if (disconnect_lanes) { WARN_ON(!dsi->vdds_dsi_enabled); @@ -1738,18 +1465,27 @@ void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) } dsi_disable_scp_clk(dsidev); - dsi_enable_pll_clock(dsidev, 0); + dsi_runtime_put(dsidev); DSSDBG("PLL uninit done\n"); } +static void dsi_pll_disable(struct dss_pll *pll) +{ + struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); + struct platform_device *dsidev = dsi->pdev; + + dsi_pll_uninit(dsidev, true); +} + static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, struct seq_file *s) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct dsi_clock_info *cinfo = &dsi->current_cinfo; + struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo; enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; int dsi_module = dsi->module_id; + struct dss_pll *pll = &dsi->pll; dispc_clk_src = dss_get_dispc_clk_source(); dsi_clk_src = dss_get_dsi_clk_source(dsi_module); @@ -1759,28 +1495,28 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); - seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin); + seq_printf(s, "dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin)); - seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); + seq_printf(s, "Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n); - seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n", - cinfo->clkin4ddr, cinfo->regm); + seq_printf(s, "CLKIN4DDR\t%-16lum %u\n", + cinfo->clkdco, cinfo->m); - seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16luregm_dispc %u\t(%s)\n", + seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n", dss_feat_get_clk_source_name(dsi_module == 0 ? OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC : OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC), - cinfo->dsi_pll_hsdiv_dispc_clk, - cinfo->regm_dispc, + cinfo->clkout[HSDIV_DISPC], + cinfo->mX[HSDIV_DISPC], dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); - seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16luregm_dsi %u\t(%s)\n", + seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n", dss_feat_get_clk_source_name(dsi_module == 0 ? OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI : OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI), - cinfo->dsi_pll_hsdiv_dsi_clk, - cinfo->regm_dsi, + cinfo->clkout[HSDIV_DSI], + cinfo->mX[HSDIV_DSI], dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); @@ -1793,11 +1529,11 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev)); seq_printf(s, "DDR_CLK\t\t%lu\n", - cinfo->clkin4ddr / 4); + cinfo->clkdco / 4); seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev)); - seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); + seq_printf(s, "LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk); dsi_runtime_put(dsidev); } @@ -2132,7 +1868,7 @@ static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns) struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); /* convert time in ns to ddr ticks, rounding up */ - unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; + unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; } @@ -2140,7 +1876,7 @@ static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; + unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; return ddr * 1000 * 1000 / (ddr_clk / 1000); } @@ -3730,7 +3466,7 @@ static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev) struct omap_video_timings *timings = &dsi->timings; int bpp = dsi_get_pixel_size(dsi->pix_fmt); int ndl = dsi->num_lanes_used - 1; - int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.regm_dsi + 1; + int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1; int hsa_interleave_hs = 0, hsa_interleave_lp = 0; int hfp_interleave_hs = 0, hfp_interleave_lp = 0; int hbp_interleave_hs = 0, hbp_interleave_lp = 0; @@ -4441,18 +4177,12 @@ static void dsi_display_uninit_dispc(struct platform_device *dsidev, static int dsi_configure_dsi_clocks(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct dsi_clock_info cinfo; + struct dss_pll_clock_info cinfo; int r; cinfo = dsi->user_dsi_cinfo; - r = dsi_calc_clock_rates(dsidev, &cinfo); - if (r) { - DSSERR("Failed to calc dsi clocks\n"); - return r; - } - - r = dsi_pll_set_clock_div(dsidev, &cinfo); + r = dss_pll_set_config(&dsi->pll, &cinfo); if (r) { DSSERR("Failed to set dsi clocks\n"); return r; @@ -4466,7 +4196,7 @@ static int dsi_display_init_dsi(struct platform_device *dsidev) struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; - r = dsi_pll_init(dsidev, true, true); + r = dss_pll_enable(&dsi->pll); if (r) goto err0; @@ -4510,7 +4240,7 @@ err3: err2: dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); err1: - dsi_pll_uninit(dsidev, true); + dss_pll_disable(&dsi->pll); err0: return r; } @@ -4551,8 +4281,6 @@ static int dsi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_get_dsi; - dsi_enable_pll_clock(dsidev, 1); - _dsi_initialize_irq(dsidev); r = dsi_display_init_dsi(dsidev); @@ -4564,7 +4292,6 @@ static int dsi_display_enable(struct omap_dss_device *dssdev) return 0; err_init_dsi: - dsi_enable_pll_clock(dsidev, 0); dsi_runtime_put(dsidev); err_get_dsi: mutex_unlock(&dsi->lock); @@ -4592,7 +4319,6 @@ static void dsi_display_disable(struct omap_dss_device *dssdev, dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps); dsi_runtime_put(dsidev); - dsi_enable_pll_clock(dsidev, 0); mutex_unlock(&dsi->lock); } @@ -4713,29 +4439,30 @@ static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck, return true; } -static bool dsi_cm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, +static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, void *data) { struct dsi_clk_calc_ctx *ctx = data; - ctx->dsi_cinfo.regm_dispc = regm_dispc; - ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc; + ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; + ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max, dsi_cm_calc_dispc_cb, ctx); } -static bool dsi_cm_calc_pll_cb(int regn, int regm, unsigned long fint, - unsigned long pll, void *data) +static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint, + unsigned long clkdco, void *data) { struct dsi_clk_calc_ctx *ctx = data; - ctx->dsi_cinfo.regn = regn; - ctx->dsi_cinfo.regm = regm; + ctx->dsi_cinfo.n = n; + ctx->dsi_cinfo.m = m; ctx->dsi_cinfo.fint = fint; - ctx->dsi_cinfo.clkin4ddr = pll; + ctx->dsi_cinfo.clkdco = clkdco; - return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->req_pck_min, + return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min, + dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), dsi_cm_calc_hsdiv_cb, ctx); } @@ -4748,7 +4475,7 @@ static bool dsi_cm_calc(struct dsi_data *dsi, unsigned long pll_min, pll_max; unsigned long pck, txbyteclk; - clkin = clk_get_rate(dsi->sys_clk); + clkin = clk_get_rate(dsi->pll.clkin); bitspp = dsi_get_pixel_size(cfg->pixel_format); ndl = dsi->num_lanes_used - 1; @@ -4764,16 +4491,16 @@ static bool dsi_cm_calc(struct dsi_data *dsi, memset(ctx, 0, sizeof(*ctx)); ctx->dsidev = dsi->pdev; + ctx->pll = &dsi->pll; ctx->config = cfg; ctx->req_pck_min = pck; ctx->req_pck_nom = pck; ctx->req_pck_max = pck * 3 / 2; - ctx->dsi_cinfo.clkin = clkin; pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4); pll_max = cfg->hs_clk_max * 4; - return dsi_pll_calc(dsi->pdev, clkin, + return dss_pll_calc(ctx->pll, clkin, pll_min, pll_max, dsi_cm_calc_pll_cb, ctx); } @@ -4784,7 +4511,7 @@ static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx) const struct omap_dss_dsi_config *cfg = ctx->config; int bitspp = dsi_get_pixel_size(cfg->pixel_format); int ndl = dsi->num_lanes_used - 1; - unsigned long hsclk = ctx->dsi_cinfo.clkin4ddr / 4; + unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4; unsigned long byteclk = hsclk / 4; unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max; @@ -4999,14 +4726,14 @@ static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck, return true; } -static bool dsi_vm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, +static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, void *data) { struct dsi_clk_calc_ctx *ctx = data; unsigned long pck_max; - ctx->dsi_cinfo.regm_dispc = regm_dispc; - ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc; + ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; + ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; /* * In burst mode we can let the dispc pck be arbitrarily high, but it @@ -5022,17 +4749,18 @@ static bool dsi_vm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc, dsi_vm_calc_dispc_cb, ctx); } -static bool dsi_vm_calc_pll_cb(int regn, int regm, unsigned long fint, - unsigned long pll, void *data) +static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint, + unsigned long clkdco, void *data) { struct dsi_clk_calc_ctx *ctx = data; - ctx->dsi_cinfo.regn = regn; - ctx->dsi_cinfo.regm = regm; + ctx->dsi_cinfo.n = n; + ctx->dsi_cinfo.m = m; ctx->dsi_cinfo.fint = fint; - ctx->dsi_cinfo.clkin4ddr = pll; + ctx->dsi_cinfo.clkdco = clkdco; - return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->req_pck_min, + return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min, + dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), dsi_vm_calc_hsdiv_cb, ctx); } @@ -5048,14 +4776,13 @@ static bool dsi_vm_calc(struct dsi_data *dsi, int bitspp = dsi_get_pixel_size(cfg->pixel_format); unsigned long byteclk_min; - clkin = clk_get_rate(dsi->sys_clk); + clkin = clk_get_rate(dsi->pll.clkin); memset(ctx, 0, sizeof(*ctx)); ctx->dsidev = dsi->pdev; + ctx->pll = &dsi->pll; ctx->config = cfg; - ctx->dsi_cinfo.clkin = clkin; - /* these limits should come from the panel driver */ ctx->req_pck_min = t->pixelclock - 1000; ctx->req_pck_nom = t->pixelclock; @@ -5074,7 +4801,7 @@ static bool dsi_vm_calc(struct dsi_data *dsi, pll_max = byteclk_max * 4 * 4; } - return dsi_pll_calc(dsi->pdev, clkin, + return dss_pll_calc(ctx->pll, clkin, pll_min, pll_max, dsi_vm_calc_pll_cb, ctx); } @@ -5106,8 +4833,8 @@ static int dsi_set_config(struct omap_dss_device *dssdev, dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo); - r = dsi_lp_clock_calc(&ctx.dsi_cinfo, config->lp_clk_min, - config->lp_clk_max); + r = dsi_lp_clock_calc(ctx.dsi_cinfo.clkout[HSDIV_DSI], + config->lp_clk_min, config->lp_clk_max, &dsi->user_lp_cinfo); if (r) { DSSERR("failed to find suitable DSI LP clock settings\n"); goto err; @@ -5234,35 +4961,6 @@ static void dsi_release_vc(struct omap_dss_device *dssdev, int channel) } } -void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) -{ - if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 7, 1) != 1) - DSSERR("%s (%s) not active\n", - dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), - dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC)); -} - -void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) -{ - if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 8, 1) != 1) - DSSERR("%s (%s) not active\n", - dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), - dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI)); -} - -static void dsi_calc_clock_param_ranges(struct platform_device *dsidev) -{ - struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - - dsi->regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN); - dsi->regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM); - dsi->regm_dispc_max = - dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC); - dsi->regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI); - dsi->fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT); - dsi->fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT); - dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); -} static int dsi_get_clocks(struct platform_device *dsidev) { @@ -5277,14 +4975,6 @@ static int dsi_get_clocks(struct platform_device *dsidev) dsi->dss_clk = clk; - clk = devm_clk_get(&dsidev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } - - dsi->sys_clk = clk; - return 0; } @@ -5453,6 +5143,135 @@ err: return r; } +static const struct dss_pll_ops dsi_pll_ops = { + .enable = dsi_pll_enable, + .disable = dsi_pll_disable, + .set_config = dss_pll_write_config_type_a, +}; + +static const struct dss_pll_hw dss_omap3_dsi_pll_hw = { + .n_max = (1 << 7) - 1, + .m_max = (1 << 11) - 1, + .mX_max = (1 << 4) - 1, + .fint_min = 750000, + .fint_max = 2100000, + .clkdco_low = 1000000000, + .clkdco_max = 1800000000, + + .n_msb = 7, + .n_lsb = 1, + .m_msb = 18, + .m_lsb = 8, + + .mX_msb[0] = 22, + .mX_lsb[0] = 19, + .mX_msb[1] = 26, + .mX_lsb[1] = 23, + + .has_stopmode = true, + .has_freqsel = true, + .has_selfreqdco = false, + .has_refsel = false, +}; + +static const struct dss_pll_hw dss_omap4_dsi_pll_hw = { + .n_max = (1 << 8) - 1, + .m_max = (1 << 12) - 1, + .mX_max = (1 << 5) - 1, + .fint_min = 500000, + .fint_max = 2500000, + .clkdco_low = 1000000000, + .clkdco_max = 1800000000, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 25, + .mX_lsb[0] = 21, + .mX_msb[1] = 30, + .mX_lsb[1] = 26, + + .has_stopmode = true, + .has_freqsel = false, + .has_selfreqdco = false, + .has_refsel = false, +}; + +static const struct dss_pll_hw dss_omap5_dsi_pll_hw = { + .n_max = (1 << 8) - 1, + .m_max = (1 << 12) - 1, + .mX_max = (1 << 5) - 1, + .fint_min = 150000, + .fint_max = 52000000, + .clkdco_low = 1000000000, + .clkdco_max = 1800000000, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 25, + .mX_lsb[0] = 21, + .mX_msb[1] = 30, + .mX_lsb[1] = 26, + + .has_stopmode = true, + .has_freqsel = false, + .has_selfreqdco = true, + .has_refsel = true, +}; + +static int dsi_init_pll_data(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dss_pll *pll = &dsi->pll; + struct clk *clk; + int r; + + clk = devm_clk_get(&dsidev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); + } + + pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1"; + pll->clkin = clk; + pll->base = dsi->pll_base; + + switch (omapdss_get_version()) { + case OMAPDSS_VER_OMAP34xx_ES1: + case OMAPDSS_VER_OMAP34xx_ES3: + case OMAPDSS_VER_OMAP3630: + case OMAPDSS_VER_AM35xx: + pll->hw = &dss_omap3_dsi_pll_hw; + break; + + case OMAPDSS_VER_OMAP4430_ES1: + case OMAPDSS_VER_OMAP4430_ES2: + case OMAPDSS_VER_OMAP4: + pll->hw = &dss_omap4_dsi_pll_hw; + break; + + case OMAPDSS_VER_OMAP5: + pll->hw = &dss_omap5_dsi_pll_hw; + break; + + default: + return -ENODEV; + } + + pll->ops = &dsi_pll_ops; + + r = dss_pll_register(pll); + if (r) + return r; + + return 0; +} + /* DSI1 HW IP initialisation */ static int omap_dsihw_probe(struct platform_device *dsidev) { @@ -5598,12 +5417,12 @@ static int omap_dsihw_probe(struct platform_device *dsidev) dsi->vc[i].vc_id = 0; } - dsi_calc_clock_param_ranges(dsidev); - r = dsi_get_clocks(dsidev); if (r) return r; + dsi_init_pll_data(dsidev); + pm_runtime_enable(&dsidev->dev); r = dsi_runtime_get(dsidev); @@ -5672,6 +5491,8 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev) WARN_ON(dsi->scp_clk_refcount > 0); + dss_pll_unregister(&dsi->pll); + dsi_uninit_output(dsidev); pm_runtime_disable(&dsidev->dev); diff --git a/drivers/video/fbdev/omap2/dss/dss-of.c b/drivers/video/fbdev/omap2/dss/dss-of.c index a4b20aa..928ee63 100644 --- a/drivers/video/fbdev/omap2/dss/dss-of.c +++ b/drivers/video/fbdev/omap2/dss/dss-of.c @@ -20,6 +20,8 @@ #include <video/omapdss.h> +#include "dss.h" + struct device_node * omapdss_of_get_next_port(const struct device_node *parent, struct device_node *prev) @@ -84,20 +86,17 @@ omapdss_of_get_next_endpoint(const struct device_node *parent, } EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint); -static struct device_node * -omapdss_of_get_remote_device_node(const struct device_node *node) +struct device_node *dss_of_port_get_parent_device(struct device_node *port) { struct device_node *np; int i; - np = of_parse_phandle(node, "remote-endpoint", 0); - - if (!np) + if (!port) return NULL; - np = of_get_next_parent(np); + np = of_get_next_parent(port); - for (i = 0; i < 3 && np; ++i) { + for (i = 0; i < 2 && np; ++i) { struct property *prop; prop = of_find_property(np, "compatible", NULL); @@ -111,6 +110,31 @@ omapdss_of_get_remote_device_node(const struct device_node *node) return NULL; } +u32 dss_of_port_get_port_number(struct device_node *port) +{ + int r; + u32 reg; + + r = of_property_read_u32(port, "reg", ®); + if (r) + reg = 0; + + return reg; +} + +static struct device_node *omapdss_of_get_remote_port(const struct device_node *node) +{ + struct device_node *np; + + np = of_parse_phandle(node, "remote-endpoint", 0); + if (!np) + return NULL; + + np = of_get_next_parent(np); + + return np; +} + struct device_node * omapdss_of_get_first_endpoint(const struct device_node *parent) { @@ -133,27 +157,25 @@ struct omap_dss_device * omapdss_of_find_source_for_first_ep(struct device_node *node) { struct device_node *ep; - struct device_node *src_node; + struct device_node *src_port; struct omap_dss_device *src; ep = omapdss_of_get_first_endpoint(node); if (!ep) return ERR_PTR(-EINVAL); - src_node = omapdss_of_get_remote_device_node(ep); - - of_node_put(ep); - - if (!src_node) + src_port = omapdss_of_get_remote_port(ep); + if (!src_port) { + of_node_put(ep); return ERR_PTR(-EINVAL); + } - src = omap_dss_find_output_by_node(src_node); + of_node_put(ep); - of_node_put(src_node); + src = omap_dss_find_output_by_port_node(src_port); - if (!src) - return ERR_PTR(-EPROBE_DEFER); + of_node_put(src_port); - return src; + return src ? src : ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep); diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c index 14bcd6c..702c495 100644 --- a/drivers/video/fbdev/omap2/dss/dss.c +++ b/drivers/video/fbdev/omap2/dss/dss.c @@ -70,7 +70,9 @@ struct dss_features { u8 fck_div_max; u8 dss_fck_multiplier; const char *parent_clk_name; - int (*dpi_select_source)(enum omap_channel channel); + enum omap_display_type *ports; + int num_ports; + int (*dpi_select_source)(int port, enum omap_channel channel); }; static struct { @@ -294,7 +296,6 @@ static void dss_dump_regs(struct seq_file *s) static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) { - struct platform_device *dsidev; int b; u8 start, end; @@ -304,13 +305,9 @@ static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: b = 1; - dsidev = dsi_get_dsidev_from_id(0); - dsi_wait_pll_hsdiv_dispc_active(dsidev); break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: b = 2; - dsidev = dsi_get_dsidev_from_id(1); - dsi_wait_pll_hsdiv_dispc_active(dsidev); break; default: BUG(); @@ -327,7 +324,6 @@ static void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) void dss_select_dsi_clk_source(int dsi_module, enum omap_dss_clk_source clk_src) { - struct platform_device *dsidev; int b, pos; switch (clk_src) { @@ -337,14 +333,10 @@ void dss_select_dsi_clk_source(int dsi_module, case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI: BUG_ON(dsi_module != 0); b = 1; - dsidev = dsi_get_dsidev_from_id(0); - dsi_wait_pll_hsdiv_dsi_active(dsidev); break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI: BUG_ON(dsi_module != 1); b = 1; - dsidev = dsi_get_dsidev_from_id(1); - dsi_wait_pll_hsdiv_dsi_active(dsidev); break; default: BUG(); @@ -360,7 +352,6 @@ void dss_select_dsi_clk_source(int dsi_module, void dss_select_lcd_clk_source(enum omap_channel channel, enum omap_dss_clk_source clk_src) { - struct platform_device *dsidev; int b, ix, pos; if (!dss_has_feature(FEAT_LCD_CLK_SRC)) { @@ -375,15 +366,11 @@ void dss_select_lcd_clk_source(enum omap_channel channel, case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: BUG_ON(channel != OMAP_DSS_CHANNEL_LCD); b = 1; - dsidev = dsi_get_dsidev_from_id(0); - dsi_wait_pll_hsdiv_dispc_active(dsidev); break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 && channel != OMAP_DSS_CHANNEL_LCD3); b = 1; - dsidev = dsi_get_dsidev_from_id(1); - dsi_wait_pll_hsdiv_dispc_active(dsidev); break; default: BUG(); @@ -564,7 +551,7 @@ enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void) return REG_GET(DSS_CONTROL, 15, 15); } -static int dss_dpi_select_source_omap2_omap3(enum omap_channel channel) +static int dss_dpi_select_source_omap2_omap3(int port, enum omap_channel channel) { if (channel != OMAP_DSS_CHANNEL_LCD) return -EINVAL; @@ -572,7 +559,7 @@ static int dss_dpi_select_source_omap2_omap3(enum omap_channel channel) return 0; } -static int dss_dpi_select_source_omap4(enum omap_channel channel) +static int dss_dpi_select_source_omap4(int port, enum omap_channel channel) { int val; @@ -592,7 +579,7 @@ static int dss_dpi_select_source_omap4(enum omap_channel channel) return 0; } -static int dss_dpi_select_source_omap5(enum omap_channel channel) +static int dss_dpi_select_source_omap5(int port, enum omap_channel channel) { int val; @@ -618,9 +605,9 @@ static int dss_dpi_select_source_omap5(enum omap_channel channel) return 0; } -int dss_dpi_select_source(enum omap_channel channel) +int dss_dpi_select_source(int port, enum omap_channel channel) { - return dss.feat->dpi_select_source(channel); + return dss.feat->dpi_select_source(port, channel); } static int dss_get_clocks(void) @@ -689,6 +676,16 @@ void dss_debug_dump_clocks(struct seq_file *s) } #endif + +static enum omap_display_type omap2plus_ports[] = { + OMAP_DISPLAY_TYPE_DPI, +}; + +static enum omap_display_type omap34xx_ports[] = { + OMAP_DISPLAY_TYPE_DPI, + OMAP_DISPLAY_TYPE_SDI, +}; + static const struct dss_features omap24xx_dss_feats __initconst = { /* * fck div max is really 16, but the divider range has gaps. The range @@ -698,6 +695,8 @@ static const struct dss_features omap24xx_dss_feats __initconst = { .dss_fck_multiplier = 2, .parent_clk_name = "core_ck", .dpi_select_source = &dss_dpi_select_source_omap2_omap3, + .ports = omap2plus_ports, + .num_ports = ARRAY_SIZE(omap2plus_ports), }; static const struct dss_features omap34xx_dss_feats __initconst = { @@ -705,6 +704,8 @@ static const struct dss_features omap34xx_dss_feats __initconst = { .dss_fck_multiplier = 2, .parent_clk_name = "dpll4_ck", .dpi_select_source = &dss_dpi_select_source_omap2_omap3, + .ports = omap34xx_ports, + .num_ports = ARRAY_SIZE(omap34xx_ports), }; static const struct dss_features omap3630_dss_feats __initconst = { @@ -712,6 +713,8 @@ static const struct dss_features omap3630_dss_feats __initconst = { .dss_fck_multiplier = 1, .parent_clk_name = "dpll4_ck", .dpi_select_source = &dss_dpi_select_source_omap2_omap3, + .ports = omap2plus_ports, + .num_ports = ARRAY_SIZE(omap2plus_ports), }; static const struct dss_features omap44xx_dss_feats __initconst = { @@ -719,6 +722,8 @@ static const struct dss_features omap44xx_dss_feats __initconst = { .dss_fck_multiplier = 1, .parent_clk_name = "dpll_per_x2_ck", .dpi_select_source = &dss_dpi_select_source_omap4, + .ports = omap2plus_ports, + .num_ports = ARRAY_SIZE(omap2plus_ports), }; static const struct dss_features omap54xx_dss_feats __initconst = { @@ -726,6 +731,8 @@ static const struct dss_features omap54xx_dss_feats __initconst = { .dss_fck_multiplier = 1, .parent_clk_name = "dpll_per_x2_ck", .dpi_select_source = &dss_dpi_select_source_omap5, + .ports = omap2plus_ports, + .num_ports = ARRAY_SIZE(omap2plus_ports), }; static const struct dss_features am43xx_dss_feats __initconst = { @@ -733,6 +740,8 @@ static const struct dss_features am43xx_dss_feats __initconst = { .dss_fck_multiplier = 0, .parent_clk_name = NULL, .dpi_select_source = &dss_dpi_select_source_omap2_omap3, + .ports = omap2plus_ports, + .num_ports = ARRAY_SIZE(omap2plus_ports), }; static int __init dss_init_features(struct platform_device *pdev) @@ -798,37 +807,77 @@ static int __init dss_init_ports(struct platform_device *pdev) if (!port) return 0; + if (dss.feat->num_ports == 0) + return 0; + do { + enum omap_display_type port_type; u32 reg; r = of_property_read_u32(port, "reg", ®); if (r) reg = 0; -#ifdef CONFIG_OMAP2_DSS_DPI - if (reg == 0) - dpi_init_port(pdev, port); -#endif + if (reg >= dss.feat->num_ports) + continue; -#ifdef CONFIG_OMAP2_DSS_SDI - if (reg == 1) - sdi_init_port(pdev, port); -#endif + port_type = dss.feat->ports[reg]; + switch (port_type) { + case OMAP_DISPLAY_TYPE_DPI: + dpi_init_port(pdev, port); + break; + case OMAP_DISPLAY_TYPE_SDI: + sdi_init_port(pdev, port); + break; + default: + break; + } } while ((port = omapdss_of_get_next_port(parent, port)) != NULL); return 0; } -static void __exit dss_uninit_ports(void) +static void __exit dss_uninit_ports(struct platform_device *pdev) { -#ifdef CONFIG_OMAP2_DSS_DPI - dpi_uninit_port(); -#endif + struct device_node *parent = pdev->dev.of_node; + struct device_node *port; -#ifdef CONFIG_OMAP2_DSS_SDI - sdi_uninit_port(); -#endif + if (parent == NULL) + return; + + port = omapdss_of_get_next_port(parent, NULL); + if (!port) + return; + + if (dss.feat->num_ports == 0) + return; + + do { + enum omap_display_type port_type; + u32 reg; + int r; + + r = of_property_read_u32(port, "reg", ®); + if (r) + reg = 0; + + if (reg >= dss.feat->num_ports) + continue; + + port_type = dss.feat->ports[reg]; + + switch (port_type) { + case OMAP_DISPLAY_TYPE_DPI: + dpi_uninit_port(port); + break; + case OMAP_DISPLAY_TYPE_SDI: + sdi_uninit_port(port); + break; + default: + break; + } + } while ((port = omapdss_of_get_next_port(parent, port)) != NULL); } /* DSS HW IP initialisation */ @@ -910,7 +959,7 @@ err_setup_clocks: static int __exit omap_dsshw_remove(struct platform_device *pdev) { - dss_uninit_ports(); + dss_uninit_ports(pdev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/video/fbdev/omap2/dss/dss.h b/drivers/video/fbdev/omap2/dss/dss.h index 8ff22c1..14fb0c2 100644 --- a/drivers/video/fbdev/omap2/dss/dss.h +++ b/drivers/video/fbdev/omap2/dss/dss.h @@ -100,35 +100,77 @@ enum dss_writeback_channel { DSS_WB_LCD3_MGR = 7, }; -struct dispc_clock_info { +struct dss_pll; + +#define DSS_PLL_MAX_HSDIVS 4 + +/* + * Type-A PLLs: clkout[]/mX[] refer to hsdiv outputs m4, m5, m6, m7. + * Type-B PLLs: clkout[0] refers to m2. + */ +struct dss_pll_clock_info { /* rates that we get with dividers below */ - unsigned long lck; - unsigned long pck; + unsigned long fint; + unsigned long clkdco; + unsigned long clkout[DSS_PLL_MAX_HSDIVS]; /* dividers */ - u16 lck_div; - u16 pck_div; + u16 n; + u16 m; + u32 mf; + u16 mX[DSS_PLL_MAX_HSDIVS]; + u16 sd; +}; + +struct dss_pll_ops { + int (*enable)(struct dss_pll *pll); + void (*disable)(struct dss_pll *pll); + int (*set_config)(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo); +}; + +struct dss_pll_hw { + unsigned n_max; + unsigned m_min; + unsigned m_max; + unsigned mX_max; + + unsigned long fint_min, fint_max; + unsigned long clkdco_min, clkdco_low, clkdco_max; + + u8 n_msb, n_lsb; + u8 m_msb, m_lsb; + u8 mX_msb[DSS_PLL_MAX_HSDIVS], mX_lsb[DSS_PLL_MAX_HSDIVS]; + + bool has_stopmode; + bool has_freqsel; + bool has_selfreqdco; + bool has_refsel; }; -struct dsi_clock_info { +struct dss_pll { + const char *name; + + struct clk *clkin; + struct regulator *regulator; + + void __iomem *base; + + const struct dss_pll_hw *hw; + + const struct dss_pll_ops *ops; + + struct dss_pll_clock_info cinfo; +}; + +struct dispc_clock_info { /* rates that we get with dividers below */ - unsigned long fint; - unsigned long clkin4ddr; - unsigned long clkin; - unsigned long dsi_pll_hsdiv_dispc_clk; /* OMAP3: DSI1_PLL_CLK - * OMAP4: PLLx_CLK1 */ - unsigned long dsi_pll_hsdiv_dsi_clk; /* OMAP3: DSI2_PLL_CLK - * OMAP4: PLLx_CLK2 */ - unsigned long lp_clk; + unsigned long lck; + unsigned long pck; /* dividers */ - u16 regn; - u16 regm; - u16 regm_dispc; /* OMAP3: REGM3 - * OMAP4: REGM4 */ - u16 regm_dsi; /* OMAP3: REGM4 - * OMAP4: REGM5 */ - u16 lp_clk_div; + u16 lck_div; + u16 pck_div; }; struct dss_lcd_mgr_config { @@ -209,12 +251,16 @@ int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); unsigned long dss_get_dispc_clk_rate(void); -int dss_dpi_select_source(enum omap_channel channel); +int dss_dpi_select_source(int port, enum omap_channel channel); void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); +/* dss-of */ +struct device_node *dss_of_port_get_parent_device(struct device_node *port); +u32 dss_of_port_get_port_number(struct device_node *port); + #if defined(CONFIG_OMAP2_DSS_DEBUGFS) void dss_debug_dump_clocks(struct seq_file *s); #endif @@ -244,16 +290,22 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min, int sdi_init_platform_driver(void) __init; void sdi_uninit_platform_driver(void) __exit; +#ifdef CONFIG_OMAP2_DSS_SDI int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init; -void sdi_uninit_port(void) __exit; +void sdi_uninit_port(struct device_node *port) __exit; +#else +static inline int __init sdi_init_port(struct platform_device *pdev, + struct device_node *port) +{ + return 0; +} +static inline void __exit sdi_uninit_port(struct device_node *port) +{ +} +#endif /* DSI */ -typedef bool (*dsi_pll_calc_func)(int regn, int regm, unsigned long fint, - unsigned long pll, void *data); -typedef bool (*dsi_hsdiv_calc_func)(int regm_dispc, unsigned long dispc, - void *data); - #ifdef CONFIG_OMAP2_DSS_DSI struct dentry; @@ -262,104 +314,36 @@ struct file_operations; int dsi_init_platform_driver(void) __init; void dsi_uninit_platform_driver(void) __exit; -int dsi_runtime_get(struct platform_device *dsidev); -void dsi_runtime_put(struct platform_device *dsidev); - void dsi_dump_clocks(struct seq_file *s); void dsi_irq_handler(void); u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt); -unsigned long dsi_get_pll_clkin(struct platform_device *dsidev); - -bool dsi_hsdiv_calc(struct platform_device *dsidev, unsigned long pll, - unsigned long out_min, dsi_hsdiv_calc_func func, void *data); -bool dsi_pll_calc(struct platform_device *dsidev, unsigned long clkin, - unsigned long pll_min, unsigned long pll_max, - dsi_pll_calc_func func, void *data); - -unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); -int dsi_pll_set_clock_div(struct platform_device *dsidev, - struct dsi_clock_info *cinfo); -int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, - bool enable_hsdiv); -void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); -void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); -void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); -struct platform_device *dsi_get_dsidev_from_id(int module); #else -static inline int dsi_runtime_get(struct platform_device *dsidev) -{ - return 0; -} -static inline void dsi_runtime_put(struct platform_device *dsidev) -{ -} static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) { WARN("%s: DSI not compiled in, returning pixel_size as 0\n", __func__); return 0; } -static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) -{ - WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); - return 0; -} -static inline int dsi_pll_set_clock_div(struct platform_device *dsidev, - struct dsi_clock_info *cinfo) -{ - WARN("%s: DSI not compiled in\n", __func__); - return -ENODEV; -} -static inline int dsi_pll_init(struct platform_device *dsidev, - bool enable_hsclk, bool enable_hsdiv) -{ - WARN("%s: DSI not compiled in\n", __func__); - return -ENODEV; -} -static inline void dsi_pll_uninit(struct platform_device *dsidev, - bool disconnect_lanes) -{ -} -static inline void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) -{ -} -static inline void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) -{ -} -static inline struct platform_device *dsi_get_dsidev_from_id(int module) -{ - return NULL; -} - -static inline unsigned long dsi_get_pll_clkin(struct platform_device *dsidev) -{ - return 0; -} - -static inline bool dsi_hsdiv_calc(struct platform_device *dsidev, - unsigned long pll, unsigned long out_min, - dsi_hsdiv_calc_func func, void *data) -{ - return false; -} - -static inline bool dsi_pll_calc(struct platform_device *dsidev, - unsigned long clkin, - unsigned long pll_min, unsigned long pll_max, - dsi_pll_calc_func func, void *data) -{ - return false; -} - #endif /* DPI */ int dpi_init_platform_driver(void) __init; void dpi_uninit_platform_driver(void) __exit; +#ifdef CONFIG_OMAP2_DSS_DPI int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init; -void dpi_uninit_port(void) __exit; +void dpi_uninit_port(struct device_node *port) __exit; +#else +static inline int __init dpi_init_port(struct platform_device *pdev, + struct device_node *port) +{ + return 0; +} +static inline void __exit dpi_uninit_port(struct device_node *port) +{ +} +#endif /* DISPC */ int dispc_init_platform_driver(void) __init; @@ -438,4 +422,29 @@ static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) } #endif +/* PLL */ +typedef bool (*dss_pll_calc_func)(int n, int m, unsigned long fint, + unsigned long clkdco, void *data); +typedef bool (*dss_hsdiv_calc_func)(int m_dispc, unsigned long dispc, + void *data); + +int dss_pll_register(struct dss_pll *pll); +void dss_pll_unregister(struct dss_pll *pll); +struct dss_pll *dss_pll_find(const char *name); +int dss_pll_enable(struct dss_pll *pll); +void dss_pll_disable(struct dss_pll *pll); +int dss_pll_set_config(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo); + +bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco, + unsigned long out_min, unsigned long out_max, + dss_hsdiv_calc_func func, void *data); +bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin, + unsigned long pll_min, unsigned long pll_max, + dss_pll_calc_func func, void *data); +int dss_pll_write_config_type_a(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo); +int dss_pll_write_config_type_b(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo); + #endif diff --git a/drivers/video/fbdev/omap2/dss/dss_features.c b/drivers/video/fbdev/omap2/dss/dss_features.c index 15088df..0e3da80 100644 --- a/drivers/video/fbdev/omap2/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/dss/dss_features.c @@ -72,10 +72,6 @@ static const struct dss_reg_field omap2_dss_reg_fields[] = { [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, [FEAT_REG_VERTICALACCU] = { 25, 16 }, [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, - [FEAT_REG_DSIPLL_REGN] = { 0, 0 }, - [FEAT_REG_DSIPLL_REGM] = { 0, 0 }, - [FEAT_REG_DSIPLL_REGM_DISPC] = { 0, 0 }, - [FEAT_REG_DSIPLL_REGM_DSI] = { 0, 0 }, }; static const struct dss_reg_field omap3_dss_reg_fields[] = { @@ -87,10 +83,6 @@ static const struct dss_reg_field omap3_dss_reg_fields[] = { [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, [FEAT_REG_VERTICALACCU] = { 25, 16 }, [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, - [FEAT_REG_DSIPLL_REGN] = { 7, 1 }, - [FEAT_REG_DSIPLL_REGM] = { 18, 8 }, - [FEAT_REG_DSIPLL_REGM_DISPC] = { 22, 19 }, - [FEAT_REG_DSIPLL_REGM_DSI] = { 26, 23 }, }; static const struct dss_reg_field am43xx_dss_reg_fields[] = { @@ -113,10 +105,6 @@ static const struct dss_reg_field omap4_dss_reg_fields[] = { [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, [FEAT_REG_VERTICALACCU] = { 26, 16 }, [FEAT_REG_DISPC_CLK_SWITCH] = { 9, 8 }, - [FEAT_REG_DSIPLL_REGN] = { 8, 1 }, - [FEAT_REG_DSIPLL_REGM] = { 20, 9 }, - [FEAT_REG_DSIPLL_REGM_DISPC] = { 25, 21 }, - [FEAT_REG_DSIPLL_REGM_DSI] = { 30, 26 }, }; static const struct dss_reg_field omap5_dss_reg_fields[] = { @@ -128,10 +116,6 @@ static const struct dss_reg_field omap5_dss_reg_fields[] = { [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, [FEAT_REG_VERTICALACCU] = { 26, 16 }, [FEAT_REG_DISPC_CLK_SWITCH] = { 9, 7 }, - [FEAT_REG_DSIPLL_REGN] = { 8, 1 }, - [FEAT_REG_DSIPLL_REGM] = { 20, 9 }, - [FEAT_REG_DSIPLL_REGM_DISPC] = { 25, 21 }, - [FEAT_REG_DSIPLL_REGM_DSI] = { 30, 26 }, }; static const enum omap_display_type omap2_dss_supported_displays[] = { @@ -437,12 +421,6 @@ static const char * const omap5_dss_clk_source_names[] = { static const struct dss_param_range omap2_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 133000000 }, [FEAT_PARAM_DSS_PCD] = { 2, 255 }, - [FEAT_PARAM_DSIPLL_REGN] = { 0, 0 }, - [FEAT_PARAM_DSIPLL_REGM] = { 0, 0 }, - [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, 0 }, - [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, 0 }, - [FEAT_PARAM_DSIPLL_FINT] = { 0, 0 }, - [FEAT_PARAM_DSIPLL_LPDIV] = { 0, 0 }, [FEAT_PARAM_DOWNSCALE] = { 1, 2 }, /* * Assuming the line width buffer to be 768 pixels as OMAP2 DISPC @@ -454,11 +432,6 @@ static const struct dss_param_range omap2_dss_param_range[] = { static const struct dss_param_range omap3_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 7) - 1 }, - [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 11) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 4) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 4) - 1 }, - [FEAT_PARAM_DSIPLL_FINT] = { 750000, 2100000 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, [FEAT_PARAM_DSI_FCK] = { 0, 173000000 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, @@ -475,11 +448,6 @@ static const struct dss_param_range am43xx_dss_param_range[] = { static const struct dss_param_range omap4_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 186000000 }, [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 8) - 1 }, - [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 12) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 5) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 5) - 1 }, - [FEAT_PARAM_DSIPLL_FINT] = { 500000, 2500000 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, [FEAT_PARAM_DSI_FCK] = { 0, 170000000 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, @@ -489,11 +457,6 @@ static const struct dss_param_range omap4_dss_param_range[] = { static const struct dss_param_range omap5_dss_param_range[] = { [FEAT_PARAM_DSS_FCK] = { 0, 209250000 }, [FEAT_PARAM_DSS_PCD] = { 1, 255 }, - [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 8) - 1 }, - [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 12) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 5) - 1 }, - [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 5) - 1 }, - [FEAT_PARAM_DSIPLL_FINT] = { 150000, 52000000 }, [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, [FEAT_PARAM_DSI_FCK] = { 0, 209250000 }, [FEAT_PARAM_DOWNSCALE] = { 1, 4 }, @@ -517,7 +480,6 @@ static const enum dss_feat_id omap3430_dss_feat_list[] = { FEAT_LINEBUFFERSPLIT, FEAT_ROWREPEATENABLE, FEAT_RESIZECONF, - FEAT_DSI_PLL_FREQSEL, FEAT_DSI_REVERSE_TXCLKESC, FEAT_VENC_REQUIRES_TV_DAC_CLK, FEAT_CPR, @@ -537,7 +499,6 @@ static const enum dss_feat_id am35xx_dss_feat_list[] = { FEAT_LINEBUFFERSPLIT, FEAT_ROWREPEATENABLE, FEAT_RESIZECONF, - FEAT_DSI_PLL_FREQSEL, FEAT_DSI_REVERSE_TXCLKESC, FEAT_VENC_REQUIRES_TV_DAC_CLK, FEAT_CPR, @@ -572,7 +533,6 @@ static const enum dss_feat_id omap3630_dss_feat_list[] = { FEAT_ROWREPEATENABLE, FEAT_RESIZECONF, FEAT_DSI_PLL_PWR_BUG, - FEAT_DSI_PLL_FREQSEL, FEAT_CPR, FEAT_PRELOAD, FEAT_FIR_COEF_V, @@ -654,8 +614,6 @@ static const enum dss_feat_id omap5_dss_feat_list[] = { FEAT_ALPHA_FREE_ZORDER, FEAT_FIFO_MERGE, FEAT_BURST_2D, - FEAT_DSI_PLL_SELFREQDCO, - FEAT_DSI_PLL_REFSEL, FEAT_DSI_PHY_DCC, FEAT_MFLAG, }; diff --git a/drivers/video/fbdev/omap2/dss/dss_features.h b/drivers/video/fbdev/omap2/dss/dss_features.h index e3ef3b7..100f7a2 100644 --- a/drivers/video/fbdev/omap2/dss/dss_features.h +++ b/drivers/video/fbdev/omap2/dss/dss_features.h @@ -41,7 +41,6 @@ enum dss_feat_id { FEAT_LCD_CLK_SRC, /* DSI-PLL power command 0x3 is not working */ FEAT_DSI_PLL_PWR_BUG, - FEAT_DSI_PLL_FREQSEL, FEAT_DSI_DCS_CMD_CONFIG_VC, FEAT_DSI_VC_OCP_WIDTH, FEAT_DSI_REVERSE_TXCLKESC, @@ -61,8 +60,6 @@ enum dss_feat_id { /* An unknown HW bug causing the normal FIFO thresholds not to work */ FEAT_OMAP3_DSI_FIFO_BUG, FEAT_BURST_2D, - FEAT_DSI_PLL_SELFREQDCO, - FEAT_DSI_PLL_REFSEL, FEAT_DSI_PHY_DCC, FEAT_MFLAG, }; @@ -77,20 +74,11 @@ enum dss_feat_reg_field { FEAT_REG_HORIZONTALACCU, FEAT_REG_VERTICALACCU, FEAT_REG_DISPC_CLK_SWITCH, - FEAT_REG_DSIPLL_REGN, - FEAT_REG_DSIPLL_REGM, - FEAT_REG_DSIPLL_REGM_DISPC, - FEAT_REG_DSIPLL_REGM_DSI, }; enum dss_range_param { FEAT_PARAM_DSS_FCK, FEAT_PARAM_DSS_PCD, - FEAT_PARAM_DSIPLL_REGN, - FEAT_PARAM_DSIPLL_REGM, - FEAT_PARAM_DSIPLL_REGM_DISPC, - FEAT_PARAM_DSIPLL_REGM_DSI, - FEAT_PARAM_DSIPLL_FINT, FEAT_PARAM_DSIPLL_LPDIV, FEAT_PARAM_DSI_FCK, FEAT_PARAM_DOWNSCALE, diff --git a/drivers/video/fbdev/omap2/dss/hdmi.h b/drivers/video/fbdev/omap2/dss/hdmi.h index 262771b..e4a32fe 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi.h +++ b/drivers/video/fbdev/omap2/dss/hdmi.h @@ -101,13 +101,6 @@ enum hdmi_core_hdmi_dvi { HDMI_HDMI = 1 }; -enum hdmi_clk_refsel { - HDMI_REFSEL_PCLK = 0, - HDMI_REFSEL_REF1 = 1, - HDMI_REFSEL_REF2 = 2, - HDMI_REFSEL_SYSCLK = 3 -}; - enum hdmi_packing_mode { HDMI_PACK_10b_RGB_YUV444 = 0, HDMI_PACK_24b_RGB_YUV444_YUV422 = 1, @@ -160,7 +153,8 @@ enum hdmi_audio_blk_strt_end_sig { enum hdmi_core_audio_layout { HDMI_AUDIO_LAYOUT_2CH = 0, - HDMI_AUDIO_LAYOUT_8CH = 1 + HDMI_AUDIO_LAYOUT_8CH = 1, + HDMI_AUDIO_LAYOUT_6CH = 2 }; enum hdmi_core_cts_mode { @@ -191,17 +185,6 @@ struct hdmi_config { enum hdmi_core_hdmi_dvi hdmi_dvi_mode; }; -/* HDMI PLL structure */ -struct hdmi_pll_info { - u16 regn; - u16 regm; - u32 regmf; - u16 regm2; - u16 regsd; - u16 dcofreq; - enum hdmi_clk_refsel refsel; -}; - struct hdmi_audio_format { enum hdmi_stereo_channels stereo_channels; u8 active_chnnls_msk; @@ -249,12 +232,15 @@ struct hdmi_core_audio_config { struct hdmi_wp_data { void __iomem *base; + phys_addr_t phys_base; }; struct hdmi_pll_data { + struct dss_pll pll; + void __iomem *base; - struct hdmi_pll_info info; + struct hdmi_wp_data *wp; }; struct hdmi_phy_data { @@ -316,16 +302,19 @@ void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, struct omap_video_timings *timings, struct hdmi_config *param); int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp); +phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp); /* HDMI PLL funcs */ -int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); -void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp); void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s); -void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy); -int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll); +void hdmi_pll_compute(struct hdmi_pll_data *pll, + unsigned long target_tmds, struct dss_pll_clock_info *pi); +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, + struct hdmi_wp_data *wp); +void hdmi_pll_uninit(struct hdmi_pll_data *hpll); /* HDMI PHY funcs */ -int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg); +int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, + unsigned long lfbitclk); void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s); int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy); int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); @@ -334,7 +323,7 @@ int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes); int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep, struct hdmi_phy_data *phy); -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) +/* Audio funcs */ int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts); int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable); int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable); @@ -342,9 +331,33 @@ void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, struct hdmi_audio_format *aud_fmt); void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, struct hdmi_audio_dma *aud_dma); -static inline bool hdmi_mode_has_audio(int mode) +static inline bool hdmi_mode_has_audio(struct hdmi_config *cfg) { - return mode == HDMI_HDMI ? true : false; + return cfg->hdmi_dvi_mode == HDMI_HDMI ? true : false; } -#endif + +/* HDMI DRV data */ +struct omap_hdmi { + struct mutex lock; + struct platform_device *pdev; + + struct hdmi_wp_data wp; + struct hdmi_pll_data pll; + struct hdmi_phy_data phy; + struct hdmi_core_data core; + + struct hdmi_config cfg; + + struct regulator *vdda_reg; + + bool core_enabled; + bool display_enabled; + + struct omap_dss_device output; + + struct platform_device *audio_pdev; + void (*audio_abort_cb)(struct device *dev); + int wp_idlemode; +}; + #endif diff --git a/drivers/video/fbdev/omap2/dss/hdmi4.c b/drivers/video/fbdev/omap2/dss/hdmi4.c index 9a8713c..f1a02bf 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/dss/hdmi4.c @@ -33,29 +33,14 @@ #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <video/omapdss.h> +#include <sound/omap-hdmi-audio.h> #include "hdmi4_core.h" #include "dss.h" #include "dss_features.h" +#include "hdmi.h" -static struct { - struct mutex lock; - struct platform_device *pdev; - - struct hdmi_wp_data wp; - struct hdmi_pll_data pll; - struct hdmi_phy_data phy; - struct hdmi_core_data core; - - struct hdmi_config cfg; - - struct clk *sys_clk; - struct regulator *vdda_hdmi_dac_reg; - - bool core_enabled; - - struct omap_dss_device output; -} hdmi; +static struct omap_hdmi hdmi; static int hdmi_runtime_get(void) { @@ -117,7 +102,7 @@ static int hdmi_init_regulator(void) int r; struct regulator *reg; - if (hdmi.vdda_hdmi_dac_reg != NULL) + if (hdmi.vdda_reg != NULL) return 0; reg = devm_regulator_get(&hdmi.pdev->dev, "vdda"); @@ -137,7 +122,7 @@ static int hdmi_init_regulator(void) } } - hdmi.vdda_hdmi_dac_reg = reg; + hdmi.vdda_reg = reg; return 0; } @@ -146,7 +131,7 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) { int r; - r = regulator_enable(hdmi.vdda_hdmi_dac_reg); + r = regulator_enable(hdmi.vdda_reg); if (r) return r; @@ -162,7 +147,7 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) return 0; err_runtime_get: - regulator_disable(hdmi.vdda_hdmi_dac_reg); + regulator_disable(hdmi.vdda_reg); return r; } @@ -172,7 +157,7 @@ static void hdmi_power_off_core(struct omap_dss_device *dssdev) hdmi.core_enabled = false; hdmi_runtime_put(); - regulator_disable(hdmi.vdda_hdmi_dac_reg); + regulator_disable(hdmi.vdda_reg); } static int hdmi_power_on_full(struct omap_dss_device *dssdev) @@ -180,8 +165,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) int r; struct omap_video_timings *p; struct omap_overlay_manager *mgr = hdmi.output.manager; - unsigned long phy; struct hdmi_wp_data *wp = &hdmi.wp; + struct dss_pll_clock_info hdmi_cinfo = { 0 }; r = hdmi_power_on_core(dssdev); if (r) @@ -195,19 +180,22 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); - /* the functions below use kHz pixel clock. TODO: change to Hz */ - phy = p->pixelclock / 1000; - - hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); + hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo); - /* config the PLL and PHY hdmi_set_pll_pwrfirst */ - r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); + r = dss_pll_enable(&hdmi.pll.pll); if (r) { - DSSDBG("Failed to lock PLL\n"); + DSSERR("Failed to enable PLL\n"); goto err_pll_enable; } - r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); + r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo); + if (r) { + DSSERR("Failed to configure PLL\n"); + goto err_pll_cfg; + } + + r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco, + hdmi_cinfo.clkout[0]); if (r) { DSSDBG("Failed to configure PHY\n"); goto err_phy_cfg; @@ -244,7 +232,8 @@ err_vid_enable: err_phy_cfg: hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: - hdmi_pll_disable(&hdmi.pll, &hdmi.wp); +err_pll_cfg: + dss_pll_disable(&hdmi.pll.pll); err_pll_enable: hdmi_power_off_core(dssdev); return -EIO; @@ -262,7 +251,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); - hdmi_pll_disable(&hdmi.pll, &hdmi.wp); + dss_pll_disable(&hdmi.pll.pll); hdmi_power_off_core(dssdev); } @@ -352,6 +341,8 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + hdmi.display_enabled = true; + mutex_unlock(&hdmi.lock); return 0; @@ -366,8 +357,13 @@ static void hdmi_display_disable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); + if (hdmi.audio_pdev && hdmi.audio_abort_cb) + hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); + hdmi_power_off_full(dssdev); + hdmi.display_enabled = false; + mutex_unlock(&hdmi.lock); } @@ -404,21 +400,6 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } -static int hdmi_get_clocks(struct platform_device *pdev) -{ - struct clk *clk; - - clk = devm_clk_get(&pdev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } - - hdmi.sys_clk = clk; - - return 0; -} - static int hdmi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { @@ -484,112 +465,6 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, return r; } -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) -static int hdmi_audio_enable(struct omap_dss_device *dssdev) -{ - int r; - - mutex_lock(&hdmi.lock); - - if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { - r = -EPERM; - goto err; - } - - r = hdmi_wp_audio_enable(&hdmi.wp, true); - if (r) - goto err; - - mutex_unlock(&hdmi.lock); - return 0; - -err: - mutex_unlock(&hdmi.lock); - return r; -} - -static void hdmi_audio_disable(struct omap_dss_device *dssdev) -{ - hdmi_wp_audio_enable(&hdmi.wp, false); -} - -static int hdmi_audio_start(struct omap_dss_device *dssdev) -{ - return hdmi4_audio_start(&hdmi.core, &hdmi.wp); -} - -static void hdmi_audio_stop(struct omap_dss_device *dssdev) -{ - hdmi4_audio_stop(&hdmi.core, &hdmi.wp); -} - -static bool hdmi_audio_supported(struct omap_dss_device *dssdev) -{ - bool r; - - mutex_lock(&hdmi.lock); - - r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); - - mutex_unlock(&hdmi.lock); - return r; -} - -static int hdmi_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - int r; - u32 pclk = hdmi.cfg.timings.pixelclock; - - mutex_lock(&hdmi.lock); - - if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { - r = -EPERM; - goto err; - } - - r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); - if (r) - goto err; - - mutex_unlock(&hdmi.lock); - return 0; - -err: - mutex_unlock(&hdmi.lock); - return r; -} -#else -static int hdmi_audio_enable(struct omap_dss_device *dssdev) -{ - return -EPERM; -} - -static void hdmi_audio_disable(struct omap_dss_device *dssdev) -{ -} - -static int hdmi_audio_start(struct omap_dss_device *dssdev) -{ - return -EPERM; -} - -static void hdmi_audio_stop(struct omap_dss_device *dssdev) -{ -} - -static bool hdmi_audio_supported(struct omap_dss_device *dssdev) -{ - return false; -} - -static int hdmi_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - return -EPERM; -} -#endif - static int hdmi_set_infoframe(struct omap_dss_device *dssdev, const struct hdmi_avi_infoframe *avi) { @@ -618,13 +493,6 @@ static const struct omapdss_hdmi_ops hdmi_ops = { .read_edid = hdmi_read_edid, .set_infoframe = hdmi_set_infoframe, .set_hdmi_mode = hdmi_set_hdmi_mode, - - .audio_enable = hdmi_audio_enable, - .audio_disable = hdmi_audio_disable, - .audio_start = hdmi_audio_start, - .audio_stop = hdmi_audio_stop, - .audio_supported = hdmi_audio_supported, - .audio_config = hdmi_audio_config, }; static void hdmi_init_output(struct platform_device *pdev) @@ -642,7 +510,7 @@ static void hdmi_init_output(struct platform_device *pdev) omapdss_register_output(out); } -static void __exit hdmi_uninit_output(struct platform_device *pdev) +static void hdmi_uninit_output(struct platform_device *pdev) { struct omap_dss_device *out = &hdmi.output; @@ -671,6 +539,112 @@ err: return r; } +/* Audio callbacks */ +static int hdmi_audio_startup(struct device *dev, + void (*abort_cb)(struct device *dev)) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&hd->lock); + + if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { + ret = -EPERM; + goto out; + } + + hd->audio_abort_cb = abort_cb; + +out: + mutex_unlock(&hd->lock); + + return ret; +} + +static int hdmi_audio_shutdown(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + mutex_lock(&hd->lock); + hd->audio_abort_cb = NULL; + mutex_unlock(&hd->lock); + + return 0; +} + +static int hdmi_audio_start(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); + WARN_ON(!hd->display_enabled); + + hdmi_wp_audio_enable(&hd->wp, true); + hdmi4_audio_start(&hd->core, &hd->wp); + + return 0; +} + +static void hdmi_audio_stop(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); + WARN_ON(!hd->display_enabled); + + hdmi4_audio_stop(&hd->core, &hd->wp); + hdmi_wp_audio_enable(&hd->wp, false); +} + +static int hdmi_audio_config(struct device *dev, + struct omap_dss_audio *dss_audio) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + int ret; + + mutex_lock(&hd->lock); + + if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { + ret = -EPERM; + goto out; + } + + ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio, + hd->cfg.timings.pixelclock); + +out: + mutex_unlock(&hd->lock); + + return ret; +} + +static const struct omap_hdmi_audio_ops hdmi_audio_ops = { + .audio_startup = hdmi_audio_startup, + .audio_shutdown = hdmi_audio_shutdown, + .audio_start = hdmi_audio_start, + .audio_stop = hdmi_audio_stop, + .audio_config = hdmi_audio_config, +}; + +static int hdmi_audio_register(struct device *dev) +{ + struct omap_hdmi_audio_pdata pdata = { + .dev = dev, + .dss_version = omapdss_get_version(), + .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), + .ops = &hdmi_audio_ops, + }; + + hdmi.audio_pdev = platform_device_register_data( + dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, + &pdata, sizeof(pdata)); + + if (IS_ERR(hdmi.audio_pdev)) + return PTR_ERR(hdmi.audio_pdev); + + return 0; +} + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { @@ -678,6 +652,7 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) int irq; hdmi.pdev = pdev; + dev_set_drvdata(&pdev->dev, &hdmi); mutex_init(&hdmi.lock); @@ -691,28 +666,23 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) if (r) return r; - r = hdmi_pll_init(pdev, &hdmi.pll); + r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp); if (r) return r; r = hdmi_phy_init(pdev, &hdmi.phy); if (r) - return r; + goto err; r = hdmi4_core_init(pdev, &hdmi.core); if (r) - return r; - - r = hdmi_get_clocks(pdev); - if (r) { - DSSERR("can't get clocks\n"); - return r; - } + goto err; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - return -ENODEV; + r = -ENODEV; + goto err; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -720,22 +690,38 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); if (r) { DSSERR("HDMI IRQ request failed\n"); - return r; + goto err; } pm_runtime_enable(&pdev->dev); hdmi_init_output(pdev); + r = hdmi_audio_register(&pdev->dev); + if (r) { + DSSERR("Registering HDMI audio failed\n"); + hdmi_uninit_output(pdev); + pm_runtime_disable(&pdev->dev); + return r; + } + dss_debugfs_create_file("hdmi", hdmi_dump_regs); return 0; +err: + hdmi_pll_uninit(&hdmi.pll); + return r; } static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) { + if (hdmi.audio_pdev) + platform_device_unregister(hdmi.audio_pdev); + hdmi_uninit_output(pdev); + hdmi_pll_uninit(&hdmi.pll); + pm_runtime_disable(&pdev->dev); return 0; @@ -743,8 +729,6 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) static int hdmi_runtime_suspend(struct device *dev) { - clk_disable_unprepare(hdmi.sys_clk); - dispc_runtime_put(); return 0; @@ -758,8 +742,6 @@ static int hdmi_runtime_resume(struct device *dev) if (r < 0) return r; - clk_prepare_enable(hdmi.sys_clk); - return 0; } diff --git a/drivers/video/fbdev/omap2/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/dss/hdmi4_core.c index 4ad39cf..7eafea5 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4_core.c +++ b/drivers/video/fbdev/omap2/dss/hdmi4_core.c @@ -31,10 +31,8 @@ #include <linux/platform_device.h> #include <linux/string.h> #include <linux/seq_file.h> -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) #include <sound/asound.h> #include <sound/asoundef.h> -#endif #include "hdmi4_core.h" #include "dss_features.h" @@ -530,7 +528,6 @@ void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s) DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID); } -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) static void hdmi_core_audio_config(struct hdmi_core_data *core, struct hdmi_core_audio_config *cfg) { @@ -877,17 +874,6 @@ void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp) hdmi_wp_audio_core_req_enable(wp, false); } -int hdmi4_audio_get_dma_port(u32 *offset, u32 *size) -{ - if (!offset || !size) - return -EINVAL; - *offset = HDMI_WP_AUDIO_DATA; - *size = 4; - return 0; -} - -#endif - int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core) { struct resource *res; diff --git a/drivers/video/fbdev/omap2/dss/hdmi4_core.h b/drivers/video/fbdev/omap2/dss/hdmi4_core.h index 827909e..a069f96 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4_core.h +++ b/drivers/video/fbdev/omap2/dss/hdmi4_core.h @@ -266,12 +266,8 @@ void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s); int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core); -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp); void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp); int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct omap_dss_audio *audio, u32 pclk); -int hdmi4_audio_get_dma_port(u32 *offset, u32 *size); -#endif - #endif diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c index 169b764..d9d0d78 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5.c @@ -38,29 +38,13 @@ #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <video/omapdss.h> +#include <sound/omap-hdmi-audio.h> #include "hdmi5_core.h" #include "dss.h" #include "dss_features.h" -static struct { - struct mutex lock; - struct platform_device *pdev; - - struct hdmi_wp_data wp; - struct hdmi_pll_data pll; - struct hdmi_phy_data phy; - struct hdmi_core_data core; - - struct hdmi_config cfg; - - struct clk *sys_clk; - struct regulator *vdda_reg; - - bool core_enabled; - - struct omap_dss_device output; -} hdmi; +static struct omap_hdmi hdmi; static int hdmi_runtime_get(void) { @@ -198,7 +182,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) int r; struct omap_video_timings *p; struct omap_overlay_manager *mgr = hdmi.output.manager; - unsigned long phy; + struct dss_pll_clock_info hdmi_cinfo = { 0 }; r = hdmi_power_on_core(dssdev); if (r) @@ -208,24 +192,27 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); - /* the functions below use kHz pixel clock. TODO: change to Hz */ - phy = p->pixelclock / 1000; - - hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy); + hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo); /* disable and clear irqs */ hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); hdmi_wp_set_irqstatus(&hdmi.wp, hdmi_wp_get_irqstatus(&hdmi.wp)); - /* config the PLL and PHY hdmi_set_pll_pwrfirst */ - r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp); + r = dss_pll_enable(&hdmi.pll.pll); if (r) { - DSSDBG("Failed to lock PLL\n"); + DSSERR("Failed to enable PLL\n"); goto err_pll_enable; } - r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg); + r = dss_pll_set_config(&hdmi.pll.pll, &hdmi_cinfo); + if (r) { + DSSERR("Failed to configure PLL\n"); + goto err_pll_cfg; + } + + r = hdmi_phy_configure(&hdmi.phy, hdmi_cinfo.clkdco, + hdmi_cinfo.clkout[0]); if (r) { DSSDBG("Failed to start PHY\n"); goto err_phy_cfg; @@ -262,7 +249,8 @@ err_vid_enable: hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: - hdmi_pll_disable(&hdmi.pll, &hdmi.wp); +err_pll_cfg: + dss_pll_disable(&hdmi.pll.pll); err_pll_enable: hdmi_power_off_core(dssdev); return -EIO; @@ -280,7 +268,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); - hdmi_pll_disable(&hdmi.pll, &hdmi.wp); + dss_pll_disable(&hdmi.pll.pll); hdmi_power_off_core(dssdev); } @@ -290,6 +278,10 @@ static int hdmi_display_check_timing(struct omap_dss_device *dssdev, { struct omap_dss_device *out = &hdmi.output; + /* TODO: proper interlace support */ + if (timings->interlace) + return -EINVAL; + if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) return -EINVAL; @@ -377,6 +369,8 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) goto err0; } + hdmi.display_enabled = true; + mutex_unlock(&hdmi.lock); return 0; @@ -391,8 +385,13 @@ static void hdmi_display_disable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); + if (hdmi.audio_pdev && hdmi.audio_abort_cb) + hdmi.audio_abort_cb(&hdmi.audio_pdev->dev); + hdmi_power_off_full(dssdev); + hdmi.display_enabled = false; + mutex_unlock(&hdmi.lock); } @@ -429,21 +428,6 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev) mutex_unlock(&hdmi.lock); } -static int hdmi_get_clocks(struct platform_device *pdev) -{ - struct clk *clk; - - clk = devm_clk_get(&pdev->dev, "sys_clk"); - if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); - return PTR_ERR(clk); - } - - hdmi.sys_clk = clk; - - return 0; -} - static int hdmi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { @@ -509,112 +493,6 @@ static int hdmi_read_edid(struct omap_dss_device *dssdev, return r; } -#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) -static int hdmi_audio_enable(struct omap_dss_device *dssdev) -{ - int r; - - mutex_lock(&hdmi.lock); - - if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { - r = -EPERM; - goto err; - } - - r = hdmi_wp_audio_enable(&hdmi.wp, true); - if (r) - goto err; - - mutex_unlock(&hdmi.lock); - return 0; - -err: - mutex_unlock(&hdmi.lock); - return r; -} - -static void hdmi_audio_disable(struct omap_dss_device *dssdev) -{ - hdmi_wp_audio_enable(&hdmi.wp, false); -} - -static int hdmi_audio_start(struct omap_dss_device *dssdev) -{ - return hdmi_wp_audio_core_req_enable(&hdmi.wp, true); -} - -static void hdmi_audio_stop(struct omap_dss_device *dssdev) -{ - hdmi_wp_audio_core_req_enable(&hdmi.wp, false); -} - -static bool hdmi_audio_supported(struct omap_dss_device *dssdev) -{ - bool r; - - mutex_lock(&hdmi.lock); - - r = hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode); - - mutex_unlock(&hdmi.lock); - return r; -} - -static int hdmi_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - int r; - u32 pclk = hdmi.cfg.timings.pixelclock; - - mutex_lock(&hdmi.lock); - - if (!hdmi_mode_has_audio(hdmi.cfg.hdmi_dvi_mode)) { - r = -EPERM; - goto err; - } - - r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, audio, pclk); - if (r) - goto err; - - mutex_unlock(&hdmi.lock); - return 0; - -err: - mutex_unlock(&hdmi.lock); - return r; -} -#else -static int hdmi_audio_enable(struct omap_dss_device *dssdev) -{ - return -EPERM; -} - -static void hdmi_audio_disable(struct omap_dss_device *dssdev) -{ -} - -static int hdmi_audio_start(struct omap_dss_device *dssdev) -{ - return -EPERM; -} - -static void hdmi_audio_stop(struct omap_dss_device *dssdev) -{ -} - -static bool hdmi_audio_supported(struct omap_dss_device *dssdev) -{ - return false; -} - -static int hdmi_audio_config(struct omap_dss_device *dssdev, - struct omap_dss_audio *audio) -{ - return -EPERM; -} -#endif - static int hdmi_set_infoframe(struct omap_dss_device *dssdev, const struct hdmi_avi_infoframe *avi) { @@ -643,13 +521,6 @@ static const struct omapdss_hdmi_ops hdmi_ops = { .read_edid = hdmi_read_edid, .set_infoframe = hdmi_set_infoframe, .set_hdmi_mode = hdmi_set_hdmi_mode, - - .audio_enable = hdmi_audio_enable, - .audio_disable = hdmi_audio_disable, - .audio_start = hdmi_audio_start, - .audio_stop = hdmi_audio_stop, - .audio_supported = hdmi_audio_supported, - .audio_config = hdmi_audio_config, }; static void hdmi_init_output(struct platform_device *pdev) @@ -667,7 +538,7 @@ static void hdmi_init_output(struct platform_device *pdev) omapdss_register_output(out); } -static void __exit hdmi_uninit_output(struct platform_device *pdev) +static void hdmi_uninit_output(struct platform_device *pdev) { struct omap_dss_device *out = &hdmi.output; @@ -696,6 +567,119 @@ err: return r; } +/* Audio callbacks */ +static int hdmi_audio_startup(struct device *dev, + void (*abort_cb)(struct device *dev)) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&hd->lock); + + if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { + ret = -EPERM; + goto out; + } + + hd->audio_abort_cb = abort_cb; + +out: + mutex_unlock(&hd->lock); + + return ret; +} + +static int hdmi_audio_shutdown(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + mutex_lock(&hd->lock); + hd->audio_abort_cb = NULL; + mutex_unlock(&hd->lock); + + return 0; +} + +static int hdmi_audio_start(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); + WARN_ON(!hd->display_enabled); + + /* No-idle while playing audio, store the old value */ + hd->wp_idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2); + REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + + hdmi_wp_audio_enable(&hd->wp, true); + hdmi_wp_audio_core_req_enable(&hd->wp, true); + + return 0; +} + +static void hdmi_audio_stop(struct device *dev) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + + WARN_ON(!hdmi_mode_has_audio(&hd->cfg)); + WARN_ON(!hd->display_enabled); + + hdmi_wp_audio_core_req_enable(&hd->wp, false); + hdmi_wp_audio_enable(&hd->wp, false); + + /* Playback stopped, restore original idlemode */ + REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); +} + +static int hdmi_audio_config(struct device *dev, + struct omap_dss_audio *dss_audio) +{ + struct omap_hdmi *hd = dev_get_drvdata(dev); + int ret; + + mutex_lock(&hd->lock); + + if (!hdmi_mode_has_audio(&hd->cfg) || !hd->display_enabled) { + ret = -EPERM; + goto out; + } + + ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio, + hd->cfg.timings.pixelclock); + +out: + mutex_unlock(&hd->lock); + + return ret; +} + +static const struct omap_hdmi_audio_ops hdmi_audio_ops = { + .audio_startup = hdmi_audio_startup, + .audio_shutdown = hdmi_audio_shutdown, + .audio_start = hdmi_audio_start, + .audio_stop = hdmi_audio_stop, + .audio_config = hdmi_audio_config, +}; + +static int hdmi_audio_register(struct device *dev) +{ + struct omap_hdmi_audio_pdata pdata = { + .dev = dev, + .dss_version = omapdss_get_version(), + .audio_dma_addr = hdmi_wp_get_audio_dma_addr(&hdmi.wp), + .ops = &hdmi_audio_ops, + }; + + hdmi.audio_pdev = platform_device_register_data( + dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, + &pdata, sizeof(pdata)); + + if (IS_ERR(hdmi.audio_pdev)) + return PTR_ERR(hdmi.audio_pdev); + + return 0; +} + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { @@ -703,6 +687,7 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) int irq; hdmi.pdev = pdev; + dev_set_drvdata(&pdev->dev, &hdmi); mutex_init(&hdmi.lock); @@ -716,28 +701,23 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) if (r) return r; - r = hdmi_pll_init(pdev, &hdmi.pll); + r = hdmi_pll_init(pdev, &hdmi.pll, &hdmi.wp); if (r) return r; r = hdmi_phy_init(pdev, &hdmi.phy); if (r) - return r; + goto err; r = hdmi5_core_init(pdev, &hdmi.core); if (r) - return r; - - r = hdmi_get_clocks(pdev); - if (r) { - DSSERR("can't get clocks\n"); - return r; - } + goto err; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - return -ENODEV; + r = -ENODEV; + goto err; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -745,22 +725,38 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp); if (r) { DSSERR("HDMI IRQ request failed\n"); - return r; + goto err; } pm_runtime_enable(&pdev->dev); hdmi_init_output(pdev); + r = hdmi_audio_register(&pdev->dev); + if (r) { + DSSERR("Registering HDMI audio failed %d\n", r); + hdmi_uninit_output(pdev); + pm_runtime_disable(&pdev->dev); + return r; + } + dss_debugfs_create_file("hdmi", hdmi_dump_regs); return 0; +err: + hdmi_pll_uninit(&hdmi.pll); + return r; } static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) { + if (hdmi.audio_pdev) + platform_device_unregister(hdmi.audio_pdev); + hdmi_uninit_output(pdev); + hdmi_pll_uninit(&hdmi.pll); + pm_runtime_disable(&pdev->dev); return 0; @@ -768,8 +764,6 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev) static int hdmi_runtime_suspend(struct device *dev) { - clk_disable_unprepare(hdmi.sys_clk); - dispc_runtime_put(); return 0; @@ -783,8 +777,6 @@ static int hdmi_runtime_resume(struct device *dev) if (r < 0) return r; - clk_prepare_enable(hdmi.sys_clk); - return 0; } diff --git a/drivers/video/fbdev/omap2/dss/hdmi5_core.c b/drivers/video/fbdev/omap2/dss/hdmi5_core.c index 83acbf7..a3cfe3d 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5_core.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5_core.c @@ -30,10 +30,8 @@ #include <linux/string.h> #include <linux/seq_file.h> #include <drm/drm_edid.h> -#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) #include <sound/asound.h> #include <sound/asoundef.h> -#endif #include "hdmi5_core.h" @@ -644,9 +642,6 @@ void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, hdmi_core_enable_interrupts(core); } - -#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) - static void hdmi5_core_audio_config(struct hdmi_core_data *core, struct hdmi_core_audio_config *cfg) { @@ -721,7 +716,7 @@ static void hdmi5_core_audio_config(struct hdmi_core_data *core, /* Source number */ val = cfg->iec60958_cfg->status[2] & IEC958_AES2_CON_SOURCE; - REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 4); + REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(2), val, 3, 0); /* Channel number right 0 */ REG_FLD_MOD(base, HDMI_CORE_FC_AUDSCHNLS(3), 2, 3, 0); @@ -879,6 +874,9 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, /* only LPCM atm */ audio_format.type = HDMI_AUDIO_TYPE_LPCM; + /* only allowed option */ + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* disable start/stop signals of IEC 60958 blocks */ audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON; @@ -894,7 +892,6 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, return 0; } -#endif int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core) { diff --git a/drivers/video/fbdev/omap2/dss/hdmi5_core.h b/drivers/video/fbdev/omap2/dss/hdmi5_core.h index ce7e9f3..f2f1022 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5_core.h +++ b/drivers/video/fbdev/omap2/dss/hdmi5_core.h @@ -299,8 +299,6 @@ void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_config *cfg); int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core); -#if defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct omap_dss_audio *audio, u32 pclk); #endif -#endif diff --git a/drivers/video/fbdev/omap2/dss/hdmi_common.c b/drivers/video/fbdev/omap2/dss/hdmi_common.c index 7d5f103..1b8fcc6 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_common.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_common.c @@ -48,7 +48,6 @@ int hdmi_parse_lanes_of(struct platform_device *pdev, struct device_node *ep, return 0; } -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts) { u32 deep_color; @@ -147,4 +146,3 @@ int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts) return 0; } -#endif diff --git a/drivers/video/fbdev/omap2/dss/hdmi_phy.c b/drivers/video/fbdev/omap2/dss/hdmi_phy.c index e007ac8..bc9e07d 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_phy.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_phy.c @@ -20,9 +20,7 @@ struct hdmi_phy_features { bool bist_ctrl; - bool calc_freqout; bool ldo_voltage; - unsigned long dcofreq_min; unsigned long max_phy; }; @@ -132,7 +130,8 @@ static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); } -int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) +int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, + unsigned long lfbitclk) { u8 freqout; @@ -149,20 +148,16 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) if (phy_feat->bist_ctrl) REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); - if (phy_feat->calc_freqout) { - /* DCOCLK/10 is pixel clock, compare pclk with DCOCLK_MIN/10 */ - u32 dco_min = phy_feat->dcofreq_min / 10; - u32 pclk = cfg->timings.pixelclock; - - if (pclk < dco_min) - freqout = 0; - else if ((pclk >= dco_min) && (pclk < phy_feat->max_phy)) - freqout = 1; - else - freqout = 2; - } else { + /* + * If the hfbitclk != lfbitclk, it means the lfbitclk was configured + * to be used for TMDS. + */ + if (hfbitclk != lfbitclk) + freqout = 0; + else if (hfbitclk / 10 < phy_feat->max_phy) freqout = 1; - } + else + freqout = 2; /* * Write to phy address 0 to configure the clock @@ -184,17 +179,13 @@ int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg) static const struct hdmi_phy_features omap44xx_phy_feats = { .bist_ctrl = false, - .calc_freqout = false, .ldo_voltage = true, - .dcofreq_min = 500000000, .max_phy = 185675000, }; static const struct hdmi_phy_features omap54xx_phy_feats = { .bist_ctrl = true, - .calc_freqout = true, .ldo_voltage = false, - .dcofreq_min = 750000000, .max_phy = 186000000, }; diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index 6d92bb3..87accdb 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c @@ -15,26 +15,13 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/clk.h> + #include <video/omapdss.h> #include "dss.h" #include "hdmi.h" -#define HDMI_DEFAULT_REGN 16 -#define HDMI_DEFAULT_REGM2 1 - -struct hdmi_pll_features { - bool sys_reset; - /* this is a hack, need to replace it with a better computation of M2 */ - bool bound_dcofreq; - unsigned long fint_min, fint_max; - u16 regm_max; - unsigned long dcofreq_low_min, dcofreq_low_max; - unsigned long dcofreq_high_min, dcofreq_high_max; -}; - -static const struct hdmi_pll_features *pll_feat; - void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) { #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ @@ -51,228 +38,189 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) DUMPPLL(PLLCTRL_CFG4); } -void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) +void hdmi_pll_compute(struct hdmi_pll_data *pll, + unsigned long target_tmds, struct dss_pll_clock_info *pi) { - struct hdmi_pll_info *pi = &pll->info; - unsigned long refclk; - u32 mf; + unsigned long fint, clkdco, clkout; + unsigned long target_bitclk, target_clkdco; + unsigned long min_dco; + unsigned n, m, mf, m2, sd; + unsigned long clkin; + const struct dss_pll_hw *hw = pll->pll.hw; - /* use our funky units */ - clkin /= 10000; + clkin = clk_get_rate(pll->pll.clkin); - /* - * Input clock is predivided by N + 1 - * out put of which is reference clk - */ + DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds); - pi->regn = HDMI_DEFAULT_REGN; + target_bitclk = target_tmds * 10; - refclk = clkin / pi->regn; + /* Fint */ + n = DIV_ROUND_UP(clkin, hw->fint_max); + fint = clkin / n; - /* temorary hack to make sure DCO freq isn't calculated too low */ - if (pll_feat->bound_dcofreq && phy <= 65000) - pi->regm2 = 3; - else - pi->regm2 = HDMI_DEFAULT_REGM2; - - /* - * multiplier is pixel_clk/ref_clk - * Multiplying by 100 to avoid fractional part removal - */ - pi->regm = phy * pi->regm2 / refclk; - - /* - * fractional multiplier is remainder of the difference between - * multiplier and actual phy(required pixel clock thus should be - * multiplied by 2^18(262144) divided by the reference clock - */ - mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; - pi->regmf = pi->regm2 * mf / refclk; - - /* - * Dcofreq should be set to 1 if required pixel clock - * is greater than 1000MHz - */ - pi->dcofreq = phy > 1000 * 100; - pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10; - - /* Set the reference clock to sysclk reference */ - pi->refsel = HDMI_REFSEL_SYSCLK; - - DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); - DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); -} + /* adjust m2 so that the clkdco will be high enough */ + min_dco = roundup(hw->clkdco_min, fint); + m2 = DIV_ROUND_UP(min_dco, target_bitclk); + if (m2 == 0) + m2 = 1; + target_clkdco = target_bitclk * m2; + m = target_clkdco / fint; -static int hdmi_pll_config(struct hdmi_pll_data *pll) -{ - u32 r; - struct hdmi_pll_info *fmt = &pll->info; + clkdco = fint * m; - /* PLL start always use manual mode */ - REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); - - r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); - r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ - r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ - hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); - - r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); - - r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ - r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ - r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ - r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ - - if (fmt->dcofreq) - r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + /* adjust clkdco with fractional mf */ + if (WARN_ON(target_clkdco - clkdco > fint)) + mf = 0; else - r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ - - hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); - - REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); + mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); - r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); - r = FLD_MOD(r, fmt->regm2, 24, 18); - r = FLD_MOD(r, fmt->regmf, 17, 0); - hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); + if (mf > 0) + clkdco += (u32)div_u64((u64)mf * fint, 262144); - /* go now */ - REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); + clkout = clkdco / m2; - /* wait for bit change */ - if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, - 0, 0, 0) != 0) { - DSSERR("PLL GO bit not clearing\n"); - return -ETIMEDOUT; - } + /* sigma-delta */ + sd = DIV_ROUND_UP(fint * m, 250000000); - /* Wait till the lock bit is set in PLL status */ - if (hdmi_wait_for_bit_change(pll->base, - PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { - DSSERR("cannot lock PLL\n"); - DSSERR("CFG1 0x%x\n", - hdmi_read_reg(pll->base, PLLCTRL_CFG1)); - DSSERR("CFG2 0x%x\n", - hdmi_read_reg(pll->base, PLLCTRL_CFG2)); - DSSERR("CFG4 0x%x\n", - hdmi_read_reg(pll->base, PLLCTRL_CFG4)); - return -ETIMEDOUT; - } + DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n", + n, m, mf, m2, sd); + DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout); - DSSDBG("PLL locked!\n"); + pi->n = n; + pi->m = m; + pi->mf = mf; + pi->mX[0] = m2; + pi->sd = sd; - return 0; + pi->fint = fint; + pi->clkdco = clkdco; + pi->clkout[0] = clkout; } -static int hdmi_pll_reset(struct hdmi_pll_data *pll) -{ - /* SYSRESET controlled by power FSM */ - REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3); - - /* READ 0x0 reset is in progress */ - if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1) - != 1) { - DSSERR("Failed to sysreset PLL\n"); - return -ETIMEDOUT; - } - - return 0; -} - -int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) +static int hdmi_pll_enable(struct dss_pll *dsspll) { + struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); + struct hdmi_wp_data *wp = pll->wp; u16 r = 0; - r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); - if (r) - return r; - r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); if (r) return r; - r = hdmi_pll_reset(pll); - if (r) - return r; - - r = hdmi_pll_config(pll); - if (r) - return r; - return 0; } -void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) +static void hdmi_pll_disable(struct dss_pll *dsspll) { + struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); + struct hdmi_wp_data *wp = pll->wp; + hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); } -static const struct hdmi_pll_features omap44xx_pll_feats = { - .sys_reset = false, - .bound_dcofreq = false, - .fint_min = 500000, - .fint_max = 2500000, - .regm_max = 4095, - .dcofreq_low_min = 500000000, - .dcofreq_low_max = 1000000000, - .dcofreq_high_min = 1000000000, - .dcofreq_high_max = 2000000000, +static const struct dss_pll_ops dsi_pll_ops = { + .enable = hdmi_pll_enable, + .disable = hdmi_pll_disable, + .set_config = dss_pll_write_config_type_b, }; -static const struct hdmi_pll_features omap54xx_pll_feats = { - .sys_reset = true, - .bound_dcofreq = true, - .fint_min = 620000, - .fint_max = 2500000, - .regm_max = 2046, - .dcofreq_low_min = 750000000, - .dcofreq_low_max = 1500000000, - .dcofreq_high_min = 1250000000, - .dcofreq_high_max = 2500000000UL, +static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { + .n_max = 255, + .m_min = 20, + .m_max = 4095, + .mX_max = 127, + .fint_min = 500000, + .fint_max = 2500000, + .clkdco_max = 1800000000, + + .clkdco_min = 500000000, + .clkdco_low = 1000000000, + .clkdco_max = 2000000000, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 24, + .mX_lsb[0] = 18, + + .has_selfreqdco = true, }; -static int hdmi_pll_init_features(struct platform_device *pdev) +static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { + .n_max = 255, + .m_min = 20, + .m_max = 2045, + .mX_max = 127, + .fint_min = 620000, + .fint_max = 2500000, + .clkdco_max = 1800000000, + + .clkdco_min = 750000000, + .clkdco_low = 1500000000, + .clkdco_max = 2500000000UL, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 24, + .mX_lsb[0] = 18, + + .has_selfreqdco = true, + .has_refsel = true, +}; + +static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll) { - struct hdmi_pll_features *dst; - const struct hdmi_pll_features *src; + struct dss_pll *pll = &hpll->pll; + struct clk *clk; + int r; - dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); - if (!dst) { - dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n"); - return -ENOMEM; + clk = devm_clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); } + pll->name = "hdmi"; + pll->base = hpll->base; + pll->clkin = clk; + switch (omapdss_get_version()) { case OMAPDSS_VER_OMAP4430_ES1: case OMAPDSS_VER_OMAP4430_ES2: case OMAPDSS_VER_OMAP4: - src = &omap44xx_pll_feats; + pll->hw = &dss_omap4_hdmi_pll_hw; break; case OMAPDSS_VER_OMAP5: - src = &omap54xx_pll_feats; + pll->hw = &dss_omap5_hdmi_pll_hw; break; default: return -ENODEV; } - memcpy(dst, src, sizeof(*dst)); - pll_feat = dst; + pll->ops = &dsi_pll_ops; + + r = dss_pll_register(pll); + if (r) + return r; return 0; } -int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, + struct hdmi_wp_data *wp) { int r; struct resource *res; - r = hdmi_pll_init_features(pdev); - if (r) - return r; + pll->wp = wp; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); if (!res) { @@ -286,5 +234,18 @@ int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) return PTR_ERR(pll->base); } + r = dsi_init_pll_data(pdev, pll); + if (r) { + DSSERR("failed to init HDMI PLL\n"); + return r; + } + return 0; } + +void hdmi_pll_uninit(struct hdmi_pll_data *hpll) +{ + struct dss_pll *pll = &hpll->pll; + + dss_pll_unregister(pll); +} diff --git a/drivers/video/fbdev/omap2/dss/hdmi_wp.c b/drivers/video/fbdev/omap2/dss/hdmi_wp.c index 496327e..c15377e 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_wp.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_wp.c @@ -185,7 +185,6 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, timings->interlace = param->timings.interlace; } -#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) || defined(CONFIG_OMAP5_DSS_HDMI_AUDIO) void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, struct hdmi_audio_format *aud_fmt) { @@ -194,8 +193,12 @@ void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, DSSDBG("Enter hdmi_wp_audio_config_format\n"); r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); - r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); - r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); + if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 || + omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 || + omapdss_get_version() == OMAPDSS_VER_OMAP4) { + r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); + r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); + } r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); r = FLD_MOD(r, aud_fmt->type, 4, 4); r = FLD_MOD(r, aud_fmt->justification, 3, 3); @@ -236,7 +239,6 @@ int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) return 0; } -#endif int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) { @@ -247,6 +249,7 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) DSSERR("can't get WP mem resource\n"); return -EINVAL; } + wp->phys_base = res->start; wp->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(wp->base)) { @@ -256,3 +259,8 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) return 0; } + +phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp) +{ + return wp->phys_base + HDMI_WP_AUDIO_DATA; +} diff --git a/drivers/video/fbdev/omap2/dss/output.c b/drivers/video/fbdev/omap2/dss/output.c index 2ab3afa..1607215 100644 --- a/drivers/video/fbdev/omap2/dss/output.c +++ b/drivers/video/fbdev/omap2/dss/output.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/of.h> #include <video/omapdss.h> @@ -131,18 +132,30 @@ struct omap_dss_device *omap_dss_find_output(const char *name) } EXPORT_SYMBOL(omap_dss_find_output); -struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node) +struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port) { + struct device_node *src_node; struct omap_dss_device *out; + u32 reg; + + src_node = dss_of_port_get_parent_device(port); + if (!src_node) + return NULL; + + reg = dss_of_port_get_port_number(port); list_for_each_entry(out, &output_list, list) { - if (out->dev->of_node == node) + if (out->dev->of_node == src_node && out->port_num == reg) { + of_node_put(src_node); return omap_dss_get_device(out); + } } + of_node_put(src_node); + return NULL; } -EXPORT_SYMBOL(omap_dss_find_output_by_node); +EXPORT_SYMBOL(omap_dss_find_output_by_port_node); struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) { diff --git a/drivers/video/fbdev/omap2/dss/pll.c b/drivers/video/fbdev/omap2/dss/pll.c new file mode 100644 index 0000000..50bc62c5 --- /dev/null +++ b/drivers/video/fbdev/omap2/dss/pll.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2014 Texas Instruments Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "PLL" + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/regulator/consumer.h> +#include <linux/sched.h> + +#include <video/omapdss.h> + +#include "dss.h" + +#define PLL_CONTROL 0x0000 +#define PLL_STATUS 0x0004 +#define PLL_GO 0x0008 +#define PLL_CONFIGURATION1 0x000C +#define PLL_CONFIGURATION2 0x0010 +#define PLL_CONFIGURATION3 0x0014 +#define PLL_SSC_CONFIGURATION1 0x0018 +#define PLL_SSC_CONFIGURATION2 0x001C +#define PLL_CONFIGURATION4 0x0020 + +static struct dss_pll *dss_plls[4]; + +int dss_pll_register(struct dss_pll *pll) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { + if (!dss_plls[i]) { + dss_plls[i] = pll; + return 0; + } + } + + return -EBUSY; +} + +void dss_pll_unregister(struct dss_pll *pll) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { + if (dss_plls[i] == pll) { + dss_plls[i] = NULL; + return; + } + } +} + +struct dss_pll *dss_pll_find(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dss_plls); ++i) { + if (dss_plls[i] && strcmp(dss_plls[i]->name, name) == 0) + return dss_plls[i]; + } + + return NULL; +} + +int dss_pll_enable(struct dss_pll *pll) +{ + int r; + + r = clk_prepare_enable(pll->clkin); + if (r) + return r; + + if (pll->regulator) { + r = regulator_enable(pll->regulator); + if (r) + goto err_reg; + } + + r = pll->ops->enable(pll); + if (r) + goto err_enable; + + return 0; + +err_enable: + regulator_disable(pll->regulator); +err_reg: + clk_disable_unprepare(pll->clkin); + return r; +} + +void dss_pll_disable(struct dss_pll *pll) +{ + pll->ops->disable(pll); + + if (pll->regulator) + regulator_disable(pll->regulator); + + clk_disable_unprepare(pll->clkin); + + memset(&pll->cinfo, 0, sizeof(pll->cinfo)); +} + +int dss_pll_set_config(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo) +{ + int r; + + r = pll->ops->set_config(pll, cinfo); + if (r) + return r; + + pll->cinfo = *cinfo; + + return 0; +} + +bool dss_pll_hsdiv_calc(const struct dss_pll *pll, unsigned long clkdco, + unsigned long out_min, unsigned long out_max, + dss_hsdiv_calc_func func, void *data) +{ + const struct dss_pll_hw *hw = pll->hw; + int m, m_start, m_stop; + unsigned long out; + + out_min = out_min ? out_min : 1; + out_max = out_max ? out_max : ULONG_MAX; + + m_start = max(DIV_ROUND_UP(clkdco, out_max), 1ul); + + m_stop = min((unsigned)(clkdco / out_min), hw->mX_max); + + for (m = m_start; m <= m_stop; ++m) { + out = clkdco / m; + + if (func(m, out, data)) + return true; + } + + return false; +} + +bool dss_pll_calc(const struct dss_pll *pll, unsigned long clkin, + unsigned long pll_min, unsigned long pll_max, + dss_pll_calc_func func, void *data) +{ + const struct dss_pll_hw *hw = pll->hw; + int n, n_start, n_stop; + int m, m_start, m_stop; + unsigned long fint, clkdco; + unsigned long pll_hw_max; + unsigned long fint_hw_min, fint_hw_max; + + pll_hw_max = hw->clkdco_max; + + fint_hw_min = hw->fint_min; + fint_hw_max = hw->fint_max; + + n_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul); + n_stop = min((unsigned)(clkin / fint_hw_min), hw->n_max); + + pll_max = pll_max ? pll_max : ULONG_MAX; + + for (n = n_start; n <= n_stop; ++n) { + fint = clkin / n; + + m_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2), + 1ul); + m_stop = min3((unsigned)(pll_max / fint / 2), + (unsigned)(pll_hw_max / fint / 2), + hw->m_max); + + for (m = m_start; m <= m_stop; ++m) { + clkdco = 2 * m * fint; + + if (func(n, m, fint, clkdco, data)) + return true; + } + } + + return false; +} + +static int wait_for_bit_change(void __iomem *reg, int bitnum, int value) +{ + unsigned long timeout; + ktime_t wait; + int t; + + /* first busyloop to see if the bit changes right away */ + t = 100; + while (t-- > 0) { + if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) + return value; + } + + /* then loop for 500ms, sleeping for 1ms in between */ + timeout = jiffies + msecs_to_jiffies(500); + while (time_before(jiffies, timeout)) { + if (FLD_GET(readl_relaxed(reg), bitnum, bitnum) == value) + return value; + + wait = ns_to_ktime(1000 * 1000); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&wait, HRTIMER_MODE_REL); + } + + return !value; +} + +static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) +{ + int t = 100; + + while (t-- > 0) { + u32 v = readl_relaxed(pll->base + PLL_STATUS); + v &= hsdiv_ack_mask; + if (v == hsdiv_ack_mask) + return 0; + } + + return -ETIMEDOUT; +} + +int dss_pll_write_config_type_a(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo) +{ + const struct dss_pll_hw *hw = pll->hw; + void __iomem *base = pll->base; + int r = 0; + u32 l; + + l = 0; + if (hw->has_stopmode) + l = FLD_MOD(l, 1, 0, 0); /* PLL_STOPMODE */ + l = FLD_MOD(l, cinfo->n - 1, hw->n_msb, hw->n_lsb); /* PLL_REGN */ + l = FLD_MOD(l, cinfo->m, hw->m_msb, hw->m_lsb); /* PLL_REGM */ + /* M4 */ + l = FLD_MOD(l, cinfo->mX[0] ? cinfo->mX[0] - 1 : 0, + hw->mX_msb[0], hw->mX_lsb[0]); + /* M5 */ + l = FLD_MOD(l, cinfo->mX[1] ? cinfo->mX[1] - 1 : 0, + hw->mX_msb[1], hw->mX_lsb[1]); + writel_relaxed(l, base + PLL_CONFIGURATION1); + + l = 0; + /* M6 */ + l = FLD_MOD(l, cinfo->mX[2] ? cinfo->mX[2] - 1 : 0, + hw->mX_msb[2], hw->mX_lsb[2]); + /* M7 */ + l = FLD_MOD(l, cinfo->mX[3] ? cinfo->mX[3] - 1 : 0, + hw->mX_msb[3], hw->mX_lsb[3]); + writel_relaxed(l, base + PLL_CONFIGURATION3); + + l = readl_relaxed(base + PLL_CONFIGURATION2); + if (hw->has_freqsel) { + u32 f = cinfo->fint < 1000000 ? 0x3 : + cinfo->fint < 1250000 ? 0x4 : + cinfo->fint < 1500000 ? 0x5 : + cinfo->fint < 1750000 ? 0x6 : + 0x7; + + l = FLD_MOD(l, f, 4, 1); /* PLL_FREQSEL */ + } else if (hw->has_selfreqdco) { + u32 f = cinfo->clkdco < hw->clkdco_low ? 0x2 : 0x4; + + l = FLD_MOD(l, f, 3, 1); /* PLL_SELFREQDCO */ + } + l = FLD_MOD(l, 1, 13, 13); /* PLL_REFEN */ + l = FLD_MOD(l, 0, 14, 14); /* PHY_CLKINEN */ + l = FLD_MOD(l, 0, 16, 16); /* M4_CLOCK_EN */ + l = FLD_MOD(l, 0, 18, 18); /* M5_CLOCK_EN */ + l = FLD_MOD(l, 1, 20, 20); /* HSDIVBYPASS */ + if (hw->has_refsel) + l = FLD_MOD(l, 3, 22, 21); /* REFSEL = sysclk */ + l = FLD_MOD(l, 0, 23, 23); /* M6_CLOCK_EN */ + l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ + writel_relaxed(l, base + PLL_CONFIGURATION2); + + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { + DSSERR("DSS DPLL GO bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock DSS DPLL\n"); + r = -EIO; + goto err; + } + + l = readl_relaxed(base + PLL_CONFIGURATION2); + l = FLD_MOD(l, 1, 14, 14); /* PHY_CLKINEN */ + l = FLD_MOD(l, cinfo->mX[0] ? 1 : 0, 16, 16); /* M4_CLOCK_EN */ + l = FLD_MOD(l, cinfo->mX[1] ? 1 : 0, 18, 18); /* M5_CLOCK_EN */ + l = FLD_MOD(l, 0, 20, 20); /* HSDIVBYPASS */ + l = FLD_MOD(l, cinfo->mX[2] ? 1 : 0, 23, 23); /* M6_CLOCK_EN */ + l = FLD_MOD(l, cinfo->mX[3] ? 1 : 0, 25, 25); /* M7_CLOCK_EN */ + writel_relaxed(l, base + PLL_CONFIGURATION2); + + r = dss_wait_hsdiv_ack(pll, + (cinfo->mX[0] ? BIT(7) : 0) | + (cinfo->mX[1] ? BIT(8) : 0) | + (cinfo->mX[2] ? BIT(10) : 0) | + (cinfo->mX[3] ? BIT(11) : 0)); + if (r) { + DSSERR("failed to enable HSDIV clocks\n"); + goto err; + } + +err: + return r; +} + +int dss_pll_write_config_type_b(struct dss_pll *pll, + const struct dss_pll_clock_info *cinfo) +{ + const struct dss_pll_hw *hw = pll->hw; + void __iomem *base = pll->base; + u32 l; + + l = 0; + l = FLD_MOD(l, cinfo->m, 20, 9); /* PLL_REGM */ + l = FLD_MOD(l, cinfo->n - 1, 8, 1); /* PLL_REGN */ + writel_relaxed(l, base + PLL_CONFIGURATION1); + + l = readl_relaxed(base + PLL_CONFIGURATION2); + l = FLD_MOD(l, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + l = FLD_MOD(l, 0x1, 13, 13); /* PLL_REFEN */ + l = FLD_MOD(l, 0x0, 14, 14); /* PHY_CLKINEN */ + if (hw->has_refsel) + l = FLD_MOD(l, 0x3, 22, 21); /* REFSEL = SYSCLK */ + + /* PLL_SELFREQDCO */ + if (cinfo->clkdco > hw->clkdco_low) + l = FLD_MOD(l, 0x4, 3, 1); + else + l = FLD_MOD(l, 0x2, 3, 1); + writel_relaxed(l, base + PLL_CONFIGURATION2); + + l = readl_relaxed(base + PLL_CONFIGURATION3); + l = FLD_MOD(l, cinfo->sd, 17, 10); /* PLL_REGSD */ + writel_relaxed(l, base + PLL_CONFIGURATION3); + + l = readl_relaxed(base + PLL_CONFIGURATION4); + l = FLD_MOD(l, cinfo->mX[0], 24, 18); /* PLL_REGM2 */ + l = FLD_MOD(l, cinfo->mf, 17, 0); /* PLL_REGM_F */ + writel_relaxed(l, base + PLL_CONFIGURATION4); + + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { + DSSERR("DSS DPLL GO bit not going down.\n"); + return -EIO; + } + + if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock DSS DPLL\n"); + return -ETIMEDOUT; + } + + return 0; +} diff --git a/drivers/video/fbdev/omap2/dss/sdi.c b/drivers/video/fbdev/omap2/dss/sdi.c index 4c9c46d..d9b10f2 100644 --- a/drivers/video/fbdev/omap2/dss/sdi.c +++ b/drivers/video/fbdev/omap2/dss/sdi.c @@ -425,7 +425,7 @@ err_datapairs: return r; } -void __exit sdi_uninit_port(void) +void __exit sdi_uninit_port(struct device_node *port) { if (!sdi.port_initialized) return; diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 210f3a0..b2ae925 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -26,6 +26,8 @@ #include <linux/module.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/of_platform.h> static struct fb_fix_screeninfo simplefb_fix = { .id = "simple", @@ -41,6 +43,8 @@ static struct fb_var_screeninfo simplefb_var = { .vmode = FB_VMODE_NONINTERLACED, }; +#define PSEUDO_PALETTE_SIZE 16 + static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { @@ -50,7 +54,7 @@ static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u32 cb = blue >> (16 - info->var.blue.length); u32 value; - if (regno >= 16) + if (regno >= PSEUDO_PALETTE_SIZE) return -EINVAL; value = (cr << info->var.red.offset) | @@ -163,11 +167,113 @@ static int simplefb_parse_pd(struct platform_device *pdev, return 0; } +struct simplefb_par { + u32 palette[PSEUDO_PALETTE_SIZE]; +#if defined CONFIG_OF && defined CONFIG_COMMON_CLK + int clk_count; + struct clk **clks; +#endif +}; + +#if defined CONFIG_OF && defined CONFIG_COMMON_CLK +/* + * Clock handling code. + * + * Here we handle the clocks property of our "simple-framebuffer" dt node. + * This is necessary so that we can make sure that any clocks needed by + * the display engine that the bootloader set up for us (and for which it + * provided a simplefb dt node), stay up, for the life of the simplefb + * driver. + * + * When the driver unloads, we cleanly disable, and then release the clocks. + * + * We only complain about errors here, no action is taken as the most likely + * error can only happen due to a mismatch between the bootloader which set + * up simplefb, and the clock definitions in the device tree. Chances are + * that there are no adverse effects, and if there are, a clean teardown of + * the fb probe will not help us much either. So just complain and carry on, + * and hope that the user actually gets a working fb at the end of things. + */ +static int simplefb_clocks_init(struct simplefb_par *par, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct clk *clock; + int i, ret; + + if (dev_get_platdata(&pdev->dev) || !np) + return 0; + + par->clk_count = of_clk_get_parent_count(np); + if (par->clk_count <= 0) + return 0; + + par->clks = kcalloc(par->clk_count, sizeof(struct clk *), GFP_KERNEL); + if (!par->clks) + return -ENOMEM; + + for (i = 0; i < par->clk_count; i++) { + clock = of_clk_get(np, i); + if (IS_ERR(clock)) { + if (PTR_ERR(clock) == -EPROBE_DEFER) { + while (--i >= 0) { + if (par->clks[i]) + clk_put(par->clks[i]); + } + kfree(par->clks); + return -EPROBE_DEFER; + } + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", + __func__, i, PTR_ERR(clock)); + continue; + } + par->clks[i] = clock; + } + + for (i = 0; i < par->clk_count; i++) { + if (par->clks[i]) { + ret = clk_prepare_enable(par->clks[i]); + if (ret) { + dev_err(&pdev->dev, + "%s: failed to enable clock %d: %d\n", + __func__, i, ret); + clk_put(par->clks[i]); + par->clks[i] = NULL; + } + } + } + + return 0; +} + +static void simplefb_clocks_destroy(struct simplefb_par *par) +{ + int i; + + if (!par->clks) + return; + + for (i = 0; i < par->clk_count; i++) { + if (par->clks[i]) { + clk_disable_unprepare(par->clks[i]); + clk_put(par->clks[i]); + } + } + + kfree(par->clks); +} +#else +static int simplefb_clocks_init(struct simplefb_par *par, + struct platform_device *pdev) { return 0; } +static void simplefb_clocks_destroy(struct simplefb_par *par) { } +#endif + static int simplefb_probe(struct platform_device *pdev) { int ret; struct simplefb_params params; struct fb_info *info; + struct simplefb_par *par; struct resource *mem; if (fb_get_options("simplefb", NULL)) @@ -188,11 +294,13 @@ static int simplefb_probe(struct platform_device *pdev) return -EINVAL; } - info = framebuffer_alloc(sizeof(u32) * 16, &pdev->dev); + info = framebuffer_alloc(sizeof(struct simplefb_par), &pdev->dev); if (!info) return -ENOMEM; platform_set_drvdata(pdev, info); + par = info->par; + info->fix = simplefb_fix; info->fix.smem_start = mem->start; info->fix.smem_len = resource_size(mem); @@ -211,8 +319,8 @@ static int simplefb_probe(struct platform_device *pdev) info->apertures = alloc_apertures(1); if (!info->apertures) { - framebuffer_release(info); - return -ENOMEM; + ret = -ENOMEM; + goto error_fb_release; } info->apertures->ranges[0].base = info->fix.smem_start; info->apertures->ranges[0].size = info->fix.smem_len; @@ -222,10 +330,14 @@ static int simplefb_probe(struct platform_device *pdev) info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len); if (!info->screen_base) { - framebuffer_release(info); - return -ENODEV; + ret = -ENOMEM; + goto error_fb_release; } - info->pseudo_palette = (void *)(info + 1); + info->pseudo_palette = par->palette; + + ret = simplefb_clocks_init(par, pdev); + if (ret < 0) + goto error_unmap; dev_info(&pdev->dev, "framebuffer at 0x%lx, 0x%x bytes, mapped to 0x%p\n", info->fix.smem_start, info->fix.smem_len, @@ -238,21 +350,29 @@ static int simplefb_probe(struct platform_device *pdev) ret = register_framebuffer(info); if (ret < 0) { dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); - iounmap(info->screen_base); - framebuffer_release(info); - return ret; + goto error_clocks; } dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); return 0; + +error_clocks: + simplefb_clocks_destroy(par); +error_unmap: + iounmap(info->screen_base); +error_fb_release: + framebuffer_release(info); + return ret; } static int simplefb_remove(struct platform_device *pdev) { struct fb_info *info = platform_get_drvdata(pdev); + struct simplefb_par *par = info->par; unregister_framebuffer(info); + simplefb_clocks_destroy(par); framebuffer_release(info); return 0; @@ -273,7 +393,27 @@ static struct platform_driver simplefb_driver = { .probe = simplefb_probe, .remove = simplefb_remove, }; -module_platform_driver(simplefb_driver); + +static int __init simplefb_init(void) +{ + int ret; + struct device_node *np; + + ret = platform_driver_register(&simplefb_driver); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_OF) && of_chosen) { + for_each_child_of_node(of_chosen, np) { + if (of_device_is_compatible(np, "simple-framebuffer")) + of_platform_device_create(np, NULL, NULL); + } + } + + return 0; +} + +fs_initcall(simplefb_init); MODULE_AUTHOR("Stephen Warren <swarren@wwwdotorg.org>"); MODULE_DESCRIPTION("Simple framebuffer driver"); |