diff options
28 files changed, 1636 insertions, 702 deletions
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt index a3bd8c050c4e..0fad7ed2ea19 100644 --- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt +++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.txt @@ -208,6 +208,7 @@ of the following host1x client modules: See ../clocks/clock-bindings.txt for details. - clock-names: Must include the following entries: - sor: clock input for the SOR hardware + - source: source clock for the SOR clock - parent: input for the pixel clock - dp: reference clock for the SOR clock - safe: safe reference for the SOR clock during power up @@ -226,9 +227,9 @@ of the following host1x client modules: - nvidia,dpaux: phandle to a DispayPort AUX interface - dpaux: DisplayPort AUX interface - - compatible: For Tegra124, must contain "nvidia,tegra124-dpaux". Otherwise, - must contain '"nvidia,<chip>-dpaux", "nvidia,tegra124-dpaux"', where - <chip> is tegra132. + - compatible : Should contain one of the following: + - "nvidia,tegra124-dpaux": for Tegra124 and Tegra132 + - "nvidia,tegra210-dpaux": for Tegra210 - reg: Physical base address and length of the controller's registers. - interrupts: The interrupt outputs from the controller. - clocks: Must contain an entry for each entry in clock-names. @@ -241,6 +242,12 @@ of the following host1x client modules: - reset-names: Must include the following entries: - dpaux - vdd-supply: phandle of a supply that powers the DisplayPort link + - i2c-bus: Subnode where I2C slave devices are listed. This subnode + must be always present. If there are no I2C slave devices, an empty + node should be added. See ../../i2c/i2c.txt for more information. + + See ../pinctrl/nvidia,tegra124-dpaux-padctl.txt for information + regarding the DPAUX pad controller bindings. Example: diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-dpaux-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-dpaux-padctl.txt new file mode 100644 index 000000000000..f2abdaee9022 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-dpaux-padctl.txt @@ -0,0 +1,60 @@ +Device tree binding for NVIDIA Tegra DPAUX pad controller +======================================================== + +The Tegra Display Port Auxiliary (DPAUX) pad controller manages two pins +which can be assigned to either the DPAUX channel or to an I2C +controller. + +This document defines the device-specific binding for the DPAUX pad +controller. Refer to pinctrl-bindings.txt in this directory for generic +information about pin controller device tree bindings. Please refer to +the binding document ../display/tegra/nvidia,tegra20-host1x.txt for more +details on the DPAUX binding. + +Pin muxing: +----------- + +Child nodes contain the pinmux configurations following the conventions +from the pinctrl-bindings.txt document. + +Since only three configurations are possible, only three child nodes are +needed to describe the pin mux'ing options for the DPAUX pads. +Furthermore, given that the pad functions are only applicable to a +single set of pads, the child nodes only need to describe the pad group +the functions are being applied to rather than the individual pads. + +Required properties: +- groups: Must be "dpaux-io" +- function: Must be either "aux", "i2c" or "off". + +Example: +-------- + + dpaux@545c0000 { + ... + + state_dpaux_aux: pinmux-aux { + groups = "dpaux-io"; + function = "aux"; + }; + + state_dpaux_i2c: pinmux-i2c { + groups = "dpaux-io"; + function = "i2c"; + }; + + state_dpaux_off: pinmux-off { + groups = "dpaux-io"; + function = "off"; + }; + }; + + ... + + i2c@7000d100 { + ... + pinctrl-0 = <&state_dpaux_i2c>; + pinctrl-1 = <&state_dpaux_off>; + pinctrl-names = "default", "idle"; + status = "disabled"; + }; diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 39940f5b7c91..8495bd01b544 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/iommu.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <soc/tegra/pmc.h> @@ -1216,6 +1217,8 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) tegra_dc_stats_reset(&dc->stats); drm_crtc_vblank_off(crtc); + + pm_runtime_put_sync(dc->dev); } static void tegra_crtc_enable(struct drm_crtc *crtc) @@ -1225,6 +1228,48 @@ static void tegra_crtc_enable(struct drm_crtc *crtc) struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; + pm_runtime_get_sync(dc->dev); + + /* initialize display controller */ + if (dc->syncpt) { + u32 syncpt = host1x_syncpt_id(dc->syncpt); + + value = SYNCPT_CNTRL_NO_STALL; + tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); + + value = SYNCPT_VSYNC_ENABLE | syncpt; + tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); + } + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); + + /* initialize timer */ + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | + WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); + + value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | + WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); + + value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); + + value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | + WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; + tegra_dc_writel(dc, value, DC_CMD_INT_MASK); + + if (dc->soc->supports_border_color) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + + /* apply PLL and pixel clock changes */ tegra_dc_commit_state(dc, state); /* program display mode */ @@ -1685,7 +1730,6 @@ static int tegra_dc_init(struct host1x_client *client) struct tegra_drm *tegra = drm->dev_private; struct drm_plane *primary = NULL; struct drm_plane *cursor = NULL; - u32 value; int err; dc->syncpt = host1x_syncpt_request(dc->dev, flags); @@ -1755,47 +1799,6 @@ static int tegra_dc_init(struct host1x_client *client) goto cleanup; } - /* initialize display controller */ - if (dc->syncpt) { - u32 syncpt = host1x_syncpt_id(dc->syncpt); - - value = SYNCPT_CNTRL_NO_STALL; - tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL); - - value = SYNCPT_VSYNC_ENABLE | syncpt; - tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC); - } - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_TYPE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY); - - /* initialize timer */ - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) | - WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY); - - value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) | - WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER); - - value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE); - - value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | - WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT; - tegra_dc_writel(dc, value, DC_CMD_INT_MASK); - - if (dc->soc->supports_border_color) - tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); - - tegra_dc_stats_reset(&dc->stats); - return 0; cleanup: @@ -1987,33 +1990,15 @@ static int tegra_dc_probe(struct platform_device *pdev) return PTR_ERR(dc->rst); } + reset_control_assert(dc->rst); + if (dc->soc->has_powergate) { if (dc->pipe == 0) dc->powergate = TEGRA_POWERGATE_DIS; else dc->powergate = TEGRA_POWERGATE_DISB; - err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, - dc->rst); - if (err < 0) { - dev_err(&pdev->dev, "failed to power partition: %d\n", - err); - return err; - } - } else { - err = clk_prepare_enable(dc->clk); - if (err < 0) { - dev_err(&pdev->dev, "failed to enable clock: %d\n", - err); - return err; - } - - err = reset_control_deassert(dc->rst); - if (err < 0) { - dev_err(&pdev->dev, "failed to deassert reset: %d\n", - err); - return err; - } + tegra_powergate_power_off(dc->powergate); } regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -2027,16 +2012,19 @@ static int tegra_dc_probe(struct platform_device *pdev) return -ENXIO; } - INIT_LIST_HEAD(&dc->client.list); - dc->client.ops = &dc_client_ops; - dc->client.dev = &pdev->dev; - err = tegra_dc_rgb_probe(dc); if (err < 0 && err != -ENODEV) { dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); return err; } + platform_set_drvdata(pdev, dc); + pm_runtime_enable(&pdev->dev); + + INIT_LIST_HEAD(&dc->client.list); + dc->client.ops = &dc_client_ops; + dc->client.dev = &pdev->dev; + err = host1x_client_register(&dc->client); if (err < 0) { dev_err(&pdev->dev, "failed to register host1x client: %d\n", @@ -2044,8 +2032,6 @@ static int tegra_dc_probe(struct platform_device *pdev) return err; } - platform_set_drvdata(pdev, dc); - return 0; } @@ -2067,7 +2053,22 @@ static int tegra_dc_remove(struct platform_device *pdev) return err; } - reset_control_assert(dc->rst); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int tegra_dc_suspend(struct device *dev) +{ + struct tegra_dc *dc = dev_get_drvdata(dev); + int err; + + err = reset_control_assert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to assert reset: %d\n", err); + return err; + } if (dc->soc->has_powergate) tegra_powergate_power_off(dc->powergate); @@ -2077,10 +2078,45 @@ static int tegra_dc_remove(struct platform_device *pdev) return 0; } +static int tegra_dc_resume(struct device *dev) +{ + struct tegra_dc *dc = dev_get_drvdata(dev); + int err; + + if (dc->soc->has_powergate) { + err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, + dc->rst); + if (err < 0) { + dev_err(dev, "failed to power partition: %d\n", err); + return err; + } + } else { + err = clk_prepare_enable(dc->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock: %d\n", err); + return err; + } + + err = reset_control_deassert(dc->rst); + if (err < 0) { + dev_err(dev, "failed to deassert reset: %d\n", err); + return err; + } + } + + return 0; +} +#endif + +static const struct dev_pm_ops tegra_dc_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL) +}; + struct platform_driver tegra_dc_driver = { .driver = { .name = "tegra-dc", .of_match_table = tegra_dc_of_match, + .pm = &tegra_dc_pm_ops, }, .probe = tegra_dc_probe, .remove = tegra_dc_remove, diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index b24a0f14821a..059f409556d5 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -12,6 +12,9 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of_gpio.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/regulator/consumer.h> @@ -44,6 +47,11 @@ struct tegra_dpaux { struct completion complete; struct work_struct work; struct list_head list; + +#ifdef CONFIG_GENERIC_PINCONF + struct pinctrl_dev *pinctrl; + struct pinctrl_desc desc; +#endif }; static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) @@ -267,6 +275,148 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data) return ret; } +enum tegra_dpaux_functions { + DPAUX_PADCTL_FUNC_AUX, + DPAUX_PADCTL_FUNC_I2C, + DPAUX_PADCTL_FUNC_OFF, +}; + +static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux) +{ + u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); + + value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; + + tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); +} + +static void tegra_dpaux_pad_power_up(struct tegra_dpaux *dpaux) +{ + u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); + + value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; + + tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); +} + +static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function) +{ + u32 value; + + switch (function) { + case DPAUX_PADCTL_FUNC_AUX: + value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | + DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | + DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | + DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | + DPAUX_HYBRID_PADCTL_MODE_AUX; + break; + + case DPAUX_PADCTL_FUNC_I2C: + value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | + DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | + DPAUX_HYBRID_PADCTL_MODE_I2C; + break; + + case DPAUX_PADCTL_FUNC_OFF: + tegra_dpaux_pad_power_down(dpaux); + return 0; + + default: + return -ENOTSUPP; + } + + tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); + tegra_dpaux_pad_power_up(dpaux); + + return 0; +} + +#ifdef CONFIG_GENERIC_PINCONF +static const struct pinctrl_pin_desc tegra_dpaux_pins[] = { + PINCTRL_PIN(0, "DP_AUX_CHx_P"), + PINCTRL_PIN(1, "DP_AUX_CHx_N"), +}; + +static const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 }; + +static const char * const tegra_dpaux_groups[] = { + "dpaux-io", +}; + +static const char * const tegra_dpaux_functions[] = { + "aux", + "i2c", + "off", +}; + +static int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl) +{ + return ARRAY_SIZE(tegra_dpaux_groups); +} + +static const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl, + unsigned int group) +{ + return tegra_dpaux_groups[group]; +} + +static int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl, + unsigned group, const unsigned **pins, + unsigned *num_pins) +{ + *pins = tegra_dpaux_pin_numbers; + *num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers); + + return 0; +} + +static const struct pinctrl_ops tegra_dpaux_pinctrl_ops = { + .get_groups_count = tegra_dpaux_get_groups_count, + .get_group_name = tegra_dpaux_get_group_name, + .get_group_pins = tegra_dpaux_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl) +{ + return ARRAY_SIZE(tegra_dpaux_functions); +} + +static const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl, + unsigned int function) +{ + return tegra_dpaux_functions[function]; +} + +static int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl, + unsigned int function, + const char * const **groups, + unsigned * const num_groups) +{ + *num_groups = ARRAY_SIZE(tegra_dpaux_groups); + *groups = tegra_dpaux_groups; + + return 0; +} + +static int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl, + unsigned int function, unsigned int group) +{ + struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pinctrl); + + return tegra_dpaux_pad_config(dpaux, function); +} + +static const struct pinmux_ops tegra_dpaux_pinmux_ops = { + .get_functions_count = tegra_dpaux_get_functions_count, + .get_function_name = tegra_dpaux_get_function_name, + .get_function_groups = tegra_dpaux_get_function_groups, + .set_mux = tegra_dpaux_set_mux, +}; +#endif + static int tegra_dpaux_probe(struct platform_device *pdev) { struct tegra_dpaux *dpaux; @@ -294,11 +444,14 @@ static int tegra_dpaux_probe(struct platform_device *pdev) return -ENXIO; } - dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); - if (IS_ERR(dpaux->rst)) { - dev_err(&pdev->dev, "failed to get reset control: %ld\n", - PTR_ERR(dpaux->rst)); - return PTR_ERR(dpaux->rst); + if (!pdev->dev.pm_domain) { + dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); + if (IS_ERR(dpaux->rst)) { + dev_err(&pdev->dev, + "failed to get reset control: %ld\n", + PTR_ERR(dpaux->rst)); + return PTR_ERR(dpaux->rst); + } } dpaux->clk = devm_clk_get(&pdev->dev, NULL); @@ -315,34 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev) return err; } - reset_control_deassert(dpaux->rst); + if (dpaux->rst) + reset_control_deassert(dpaux->rst); dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); if (IS_ERR(dpaux->clk_parent)) { dev_err(&pdev->dev, "failed to get parent clock: %ld\n", PTR_ERR(dpaux->clk_parent)); - return PTR_ERR(dpaux->clk_parent); + err = PTR_ERR(dpaux->clk_parent); + goto assert_reset; } err = clk_prepare_enable(dpaux->clk_parent); if (err < 0) { dev_err(&pdev->dev, "failed to enable parent clock: %d\n", err); - return err; + goto assert_reset; } err = clk_set_rate(dpaux->clk_parent, 270000000); if (err < 0) { dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", err); - return err; + goto disable_parent_clk; } dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(dpaux->vdd)) { dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", PTR_ERR(dpaux->vdd)); - return PTR_ERR(dpaux->vdd); + err = PTR_ERR(dpaux->vdd); + goto disable_parent_clk; } err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, @@ -350,7 +506,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) if (err < 0) { dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", dpaux->irq, err); - return err; + goto disable_parent_clk; } disable_irq(dpaux->irq); @@ -360,7 +516,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) err = drm_dp_aux_register(&dpaux->aux); if (err < 0) - return err; + goto disable_parent_clk; /* * Assume that by default the DPAUX/I2C pads will be used for HDMI, @@ -370,16 +526,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev) * is no possibility to perform the I2C mode configuration in the * HDMI path. */ - value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); - value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); - - value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_PADCTL); - value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | - DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | - DPAUX_HYBRID_PADCTL_MODE_I2C; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); + err = tegra_dpaux_pad_config(dpaux, DPAUX_HYBRID_PADCTL_MODE_I2C); + if (err < 0) + return err; +#ifdef CONFIG_GENERIC_PINCONF + dpaux->desc.name = dev_name(&pdev->dev); + dpaux->desc.pins = tegra_dpaux_pins; + dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins); + dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops; + dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops; + dpaux->desc.owner = THIS_MODULE; + + dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux); + if (!dpaux->pinctrl) { + dev_err(&pdev->dev, "failed to register pincontrol\n"); + return -ENODEV; + } +#endif /* enable and clear all interrupts */ value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT | DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT; @@ -393,17 +557,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dpaux); return 0; + +disable_parent_clk: + clk_disable_unprepare(dpaux->clk_parent); +assert_reset: + if (dpaux->rst) + reset_control_assert(dpaux->rst); + + clk_disable_unprepare(dpaux->clk); + + return err; } static int tegra_dpaux_remove(struct platform_device *pdev) { struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); - u32 value; /* make sure pads are powered down when not in use */ - value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); - value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); + tegra_dpaux_pad_power_down(dpaux); drm_dp_aux_unregister(&dpaux->aux); @@ -414,7 +585,10 @@ static int tegra_dpaux_remove(struct platform_device *pdev) cancel_work_sync(&dpaux->work); clk_disable_unprepare(dpaux->clk_parent); - reset_control_assert(dpaux->rst); + + if (dpaux->rst) + reset_control_assert(dpaux->rst); + clk_disable_unprepare(dpaux->clk); return 0; @@ -528,30 +702,15 @@ enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux) int drm_dp_aux_enable(struct drm_dp_aux *aux) { struct tegra_dpaux *dpaux = to_dpaux(aux); - u32 value; - - value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | - DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | - DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | - DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | - DPAUX_HYBRID_PADCTL_MODE_AUX; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); - - value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); - value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); - return 0; + return tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_AUX); } int drm_dp_aux_disable(struct drm_dp_aux *aux) { struct tegra_dpaux *dpaux = to_dpaux(aux); - u32 value; - value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); - value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; - tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); + tegra_dpaux_pad_power_down(dpaux); return 0; } diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index a177a42a9849..755264d9db22 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -56,8 +56,8 @@ static void tegra_atomic_complete(struct tegra_drm *tegra, */ drm_atomic_helper_commit_modeset_disables(drm, state); - drm_atomic_helper_commit_planes(drm, state, false); drm_atomic_helper_commit_modeset_enables(drm, state); + drm_atomic_helper_commit_planes(drm, state, true); drm_atomic_helper_wait_for_vblanks(drm, state); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 099cccb2fbcb..3d228ad90e0f 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/regulator/consumer.h> @@ -677,6 +678,45 @@ static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); } +static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) +{ + u32 value; + + value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); + + return 0; +} + +static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) +{ + u32 value; + + /* + * XXX Is this still needed? The module reset is deasserted right + * before this function is called. + */ + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); + + /* start calibration */ + tegra_dsi_pad_enable(dsi); + + value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | + DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | + DSI_PAD_OUT_CLK(0x0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); + + value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); + + return tegra_mipi_calibrate(dsi->mipi); +} + static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, unsigned int vrefresh) { @@ -836,7 +876,7 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) tegra_dsi_disable(dsi); - return; + pm_runtime_put(dsi->dev); } static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) @@ -847,6 +887,13 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) struct tegra_dsi *dsi = to_dsi(output); struct tegra_dsi_state *state; u32 value; + int err; + + pm_runtime_get_sync(dsi->dev); + + err = tegra_dsi_pad_calibrate(dsi); + if (err < 0) + dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); state = tegra_dsi_get_state(dsi); @@ -875,8 +922,6 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) if (output->panel) drm_panel_enable(output->panel); - - return; } static int @@ -966,55 +1011,12 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { .atomic_check = tegra_dsi_encoder_atomic_check, }; -static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) -{ - u32 value; - - value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); - - return 0; -} - -static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) -{ - u32 value; - - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); - - /* start calibration */ - tegra_dsi_pad_enable(dsi); - - value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | - DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | - DSI_PAD_OUT_CLK(0x0); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); - - value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | - DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); - - return tegra_mipi_calibrate(dsi->mipi); -} - static int tegra_dsi_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); struct tegra_dsi *dsi = host1x_client_to_dsi(client); int err; - reset_control_deassert(dsi->rst); - - err = tegra_dsi_pad_calibrate(dsi); - if (err < 0) { - dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); - goto reset; - } - /* Gangsters must not register their own outputs. */ if (!dsi->master) { dsi->output.dev = client-& |
