2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-24 13:13:57 +08:00

drm/tegra: Changes for v4.8-rc1

This set of changes contains a bunch of cleanups to the host1x driver as
 well as the addition of a pin controller for DPAUX, which is required by
 boards to configure the DPAUX pads in AUX mode (for DisplayPort) or I2C
 mode (for HDMI and DDC).
 
 Included is also a bit of rework of the SOR driver in preparation to add
 DisplayPort support as well as some refactoring and cleanup.
 
 Finally, all output drivers are converted to runtime PM, which greatly
 simplifies the handling of clocks and resets.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJXh5n1AAoJEN0jrNd/PrOh+KcP/0SLO9UKvGBsfD0EFvkxx/NV
 VZvqCWP0ARCGDAZn890O4sqwgHdRDF+hp0HcUt8wDt4o3VTYJIdifr/5tunOZ6+w
 1IatWrjJjpyvZs9bveEvtJ8Hbkq5h0esA1StcqheRUiE0o+uQppdWO1NLO+HxssQ
 f4qV4GQ7trJQdjr9OGfGJigsvOjwGVbPL7CRrKKLmbfTiqy9zhi5bOyUfojtMEL6
 BvH9RMqC0ke70sGtcPgigbL7wgnbkH0uR3sV5TwU5dErrvh0tnhYU1a1ednPotJW
 z56+W8hWfC3XLIiU9mju1l+3sQA0BuS9ZQ+6VhdRTKxv85CUI5dUKT0/h5BtRrK9
 0XgPwrn4YpAr17oFEGdqFPhefkDkbb/1WrWmgUEi8/YaimdCXNkf8rILZvWUVQdt
 q9GbbmEiGNR2FvBEN7s/nT96E5XEzfLTjOzTOP0c5RF9cOK+Ul9t0t44eBn4Wqbk
 x6rTv37eieK5RVYd24xKKnzf5uSMpU+RTea4Vpr0xdH6glGcrTJ5ogub0iIsY0Jn
 A9M1pKadQvu//95ZzGWEyrqAr66cDG8vGjZn9z24PRaNZEbzvY3ohj2/qOSQoRFY
 HPmswshI0DnyFPWmdhhlPy3di0zNDP6kfPo6NhotoAl2Uwd+RrY/VyWjDG0dwV0R
 nSkcnGES5hnf3gguXEmO
 =7xlG
 -----END PGP SIGNATURE-----

Merge tag 'drm/tegra/for-4.8-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v4.8-rc1

This set of changes contains a bunch of cleanups to the host1x driver as
well as the addition of a pin controller for DPAUX, which is required by
boards to configure the DPAUX pads in AUX mode (for DisplayPort) or I2C
mode (for HDMI and DDC).

Included is also a bit of rework of the SOR driver in preparation to add
DisplayPort support as well as some refactoring and cleanup.

Finally, all output drivers are converted to runtime PM, which greatly
simplifies the handling of clocks and resets.

* tag 'drm/tegra/for-4.8-rc1' of git://anongit.freedesktop.org/tegra/linux: (35 commits)
  drm/tegra: sor: Reject HDMI 2.0 modes
  drm/tegra: sor: Prepare for generic PM domain support
  drm/tegra: dsi: Prepare for generic PM domain support
  drm/tegra: sor: Make XBAR configurable per SoC
  drm/tegra: sor: Use sor1_src clock to set parent for HDMI
  dt-bindings: display: tegra: Add source clock for SOR
  drm/tegra: sor: Implement sor1_brick clock
  drm/tegra: sor: Implement runtime PM
  drm/tegra: hdmi: Implement runtime PM
  drm/tegra: dsi: Implement runtime PM
  drm/tegra: dc: Implement runtime PM
  drm/tegra: hdmi: Enable audio over HDMI
  drm/tegra: sor: Do not support deep color modes
  drm/tegra: sor: Extract tegra_sor_mode_set()
  drm/tegra: sor: Split out tegra_sor_apply_config()
  drm/tegra: sor: Rename tegra_sor_calc_config()
  drm/tegra: sor: Factor out tegra_sor_set_parent_clock()
  drm/tegra: dpaux: Add pinctrl support
  dt-bindings: Add bindings for Tegra DPAUX pinctrl driver
  drm/tegra: Prepare DPAUX for supporting generic PM domains
  ...
This commit is contained in:
Dave Airlie 2016-07-16 11:23:50 +10:00
commit 877fa9a42d
28 changed files with 1641 additions and 707 deletions

View File

@ -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:

View File

@ -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";
};

View File

@ -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,

View File

@ -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,12 +444,15 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
return -ENXIO;
}
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",
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);
if (IS_ERR(dpaux->clk)) {
@ -315,34 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
return err;
}
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);
err = tegra_dpaux_pad_config(dpaux, DPAUX_HYBRID_PADCTL_MODE_I2C);
if (err < 0)
return err;
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);
#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);
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;
}

View File

@ -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);

View File

@ -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->dev;
@ -1037,12 +1039,9 @@ static int tegra_dsi_init(struct host1x_client *client)
drm_connector_register(&dsi->output.connector);
err = tegra_output_init(drm, &dsi->output);
if (err < 0) {
dev_err(client->dev,
"failed to initialize output: %d\n",
if (err < 0)
dev_err(dsi->dev, "failed to initialize output: %d\n",
err);
goto reset;
}
dsi->output.encoder.possible_crtcs = 0x3;
}
@ -1054,10 +1053,6 @@ static int tegra_dsi_init(struct host1x_client *client)
}
return 0;
reset:
reset_control_assert(dsi->rst);
return err;
}
static int tegra_dsi_exit(struct host1x_client *client)
@ -1069,7 +1064,7 @@ static int tegra_dsi_exit(struct host1x_client *client)
if (IS_ENABLED(CONFIG_DEBUG_FS))
tegra_dsi_debugfs_exit(dsi);
reset_control_assert(dsi->rst);
regulator_disable(dsi->vdd);
return 0;
}
@ -1493,74 +1488,50 @@ static int tegra_dsi_probe(struct platform_device *pdev)
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 4;
if (!pdev->dev.pm_domain) {
dsi->rst = devm_reset_control_get(&pdev->dev, "dsi");
if (IS_ERR(dsi->rst))
return PTR_ERR(dsi->rst);
}
dsi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dsi->clk)) {
dev_err(&pdev->dev, "cannot get DSI clock\n");
err = PTR_ERR(dsi->clk);
goto reset;
}
err = clk_prepare_enable(dsi->clk);
if (err < 0) {
dev_err(&pdev->dev, "cannot enable DSI clock\n");
goto reset;
return PTR_ERR(dsi->clk);
}
dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");
if (IS_ERR(dsi->clk_lp)) {
dev_err(&pdev->dev, "cannot get low-power clock\n");
err = PTR_ERR(dsi->clk_lp);
goto disable_clk;
}
err = clk_prepare_enable(dsi->clk_lp);
if (err < 0) {
dev_err(&pdev->dev, "cannot enable low-power clock\n");
goto disable_clk;
return PTR_ERR(dsi->clk_lp);
}
dsi->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(dsi->clk_parent)) {
dev_err(&pdev->dev, "cannot get parent clock\n");
err = PTR_ERR(dsi->clk_parent);
goto disable_clk_lp;
return PTR_ERR(dsi->clk_parent);
}
dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
if (IS_ERR(dsi->vdd)) {
dev_err(&pdev->dev, "cannot get VDD supply\n");
err = PTR_ERR(dsi->vdd);
goto disable_clk_lp;
}
err = regulator_enable(dsi->vdd);
if (err < 0) {
dev_err(&pdev->dev, "cannot enable VDD supply\n");
goto disable_clk_lp;
return PTR_ERR(dsi->vdd);
}
err = tegra_dsi_setup_clocks(dsi);
if (err < 0) {
dev_err(&pdev->dev, "cannot setup clocks\n");
goto disable_vdd;
return err;
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dsi->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(dsi->regs)) {
err = PTR_ERR(dsi->regs);
goto disable_vdd;
}
if (IS_ERR(dsi->regs))
return PTR_ERR(dsi->regs);
dsi->mipi = tegra_mipi_request(&pdev->dev);
if (IS_ERR(dsi->mipi)) {
err = PTR_ERR(dsi->mipi);
goto disable_vdd;
}
if (IS_ERR(dsi->mipi))
return PTR_ERR(dsi->mipi);
dsi->host.ops = &tegra_dsi_host_ops;
dsi->host.dev = &pdev->dev;
@ -1571,6 +1542,9 @@ static int tegra_dsi_probe(struct platform_device *pdev)
goto mipi_free;
}
platform_set_drvdata(pdev, dsi);
pm_runtime_enable(&pdev->dev);
INIT_LIST_HEAD(&dsi->client.list);
dsi->client.ops = &dsi_client_ops;
dsi->client.dev = &pdev->dev;
@ -1582,22 +1556,12 @@ static int tegra_dsi_probe(struct platform_device *pdev)
goto unregister;
}
platform_set_drvdata(pdev, dsi);
return 0;
unregister:
mipi_dsi_host_unregister(&dsi->host);
mipi_free:
tegra_mipi_free(dsi->mipi);
disable_vdd:
regulator_disable(dsi->vdd);
disable_clk_lp:
clk_disable_unprepare(dsi->clk_lp);
disable_clk:
clk_disable_unprepare(dsi->clk);
reset:
reset_control_assert(dsi->rst);
return err;
}
@ -1606,6 +1570,8 @@ static int tegra_dsi_remove(struct platform_device *pdev)
struct tegra_dsi *dsi = platform_get_drvdata(pdev);
int err;
pm_runtime_disable(&pdev->dev);
err = host1x_client_unregister(&dsi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@ -1618,14 +1584,82 @@ static int tegra_dsi_remove(struct platform_device *pdev)
mipi_dsi_host_unregister(&dsi->host);
tegra_mipi_free(dsi->mipi);
regulator_disable(dsi->vdd);
return 0;
}
#ifdef CONFIG_PM
static int tegra_dsi_suspend(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
int err;
if (dsi->rst) {
err = reset_control_assert(dsi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
}
usleep_range(1000, 2000);
clk_disable_unprepare(dsi->clk_lp);
clk_disable_unprepare(dsi->clk);
reset_control_assert(dsi->rst);
regulator_disable(dsi->vdd);
return 0;
}
static int tegra_dsi_resume(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
int err;
err = regulator_enable(dsi->vdd);
if (err < 0) {
dev_err(dsi->dev, "failed to enable VDD supply: %d\n", err);
return err;
}
err = clk_prepare_enable(dsi->clk);
if (err < 0) {
dev_err(dev, "cannot enable DSI clock: %d\n", err);
goto disable_vdd;
}
err = clk_prepare_enable(dsi->clk_lp);
if (err < 0) {
dev_err(dev, "cannot enable low-power clock: %d\n", err);
goto disable_clk;
}
usleep_range(1000, 2000);
if (dsi->rst) {
err = reset_control_deassert(dsi->rst);
if (err < 0) {
dev_err(dev, "cannot assert reset: %d\n", err);
goto disable_clk_lp;
}
}
return 0;
disable_clk_lp:
clk_disable_unprepare(dsi->clk_lp);
disable_clk:
clk_disable_unprepare(dsi->clk);
disable_vdd:
regulator_disable(dsi->vdd);
return err;
}
#endif
static const struct dev_pm_ops tegra_dsi_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dsi_suspend, tegra_dsi_resume, NULL)
};
static const struct of_device_id tegra_dsi_of_match[] = {
{ .compatible = "nvidia,tegra210-dsi", },
{ .compatible = "nvidia,tegra132-dsi", },
@ -1639,6 +1673,7 @@ struct platform_driver tegra_dsi_driver = {
.driver = {
.name = "tegra-dsi",
.of_match_table = tegra_dsi_of_match,
.pm = &tegra_dsi_pm_ops,
},
.probe = tegra_dsi_probe,
.remove = tegra_dsi_remove,

View File

@ -11,6 +11,7 @@
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/hdmi.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@ -18,10 +19,14 @@
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <sound/hda_verbs.h>
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
#define HDMI_ELD_BUFFER_SIZE 96
struct tmds_config {
unsigned int pclk;
u32 pll0;
@ -39,6 +44,8 @@ struct tegra_hdmi_config {
u32 fuse_override_value;
bool has_sor_io_peak_current;
bool has_hda;
bool has_hbr;
};
struct tegra_hdmi {
@ -60,7 +67,10 @@ struct tegra_hdmi {
const struct tegra_hdmi_config *config;
unsigned int audio_source;
unsigned int audio_freq;
unsigned int audio_sample_rate;
unsigned int audio_channels;
unsigned int pixel_clock;
bool stereo;
bool dvi;
@ -402,11 +412,11 @@ static const struct tmds_config tegra124_tmds_config[] = {
};
static const struct tegra_hdmi_audio_config *
tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
tegra_hdmi_get_audio_config(unsigned int sample_rate, unsigned int pclk)
{
const struct tegra_hdmi_audio_config *table;
switch (audio_freq) {
switch (sample_rate) {
case 32000:
table = tegra_hdmi_audio_32k;
break;
@ -476,44 +486,114 @@ static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi)
}
}
static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
static void tegra_hdmi_write_aval(struct tegra_hdmi *hdmi, u32 value)
{
static const struct {
unsigned int sample_rate;
unsigned int offset;
} regs[] = {
{ 32000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 },
{ 44100, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 },
{ 48000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480 },
{ 88200, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882 },
{ 96000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960 },
{ 176400, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764 },
{ 192000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 },
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
if (regs[i].sample_rate == hdmi->audio_sample_rate) {
tegra_hdmi_writel(hdmi, value, regs[i].offset);
break;
}
}
}
static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi)
{
struct device_node *node = hdmi->dev->of_node;
const struct tegra_hdmi_audio_config *config;
unsigned int offset = 0;
u32 value;
u32 source, value;
switch (hdmi->audio_source) {
case HDA:
value = AUDIO_CNTRL0_SOURCE_SELECT_HDAL;
if (hdmi->config->has_hda)
source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL;
else
return -EINVAL;
break;
case SPDIF:
value = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF;
if (hdmi->config->has_hda)
source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF;
else
source = AUDIO_CNTRL0_SOURCE_SELECT_SPDIF;
break;
default:
value = AUDIO_CNTRL0_SOURCE_SELECT_AUTO;
if (hdmi->config->has_hda)
source = SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO;
else
source = AUDIO_CNTRL0_SOURCE_SELECT_AUTO;
break;
}
if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) {
value |= AUDIO_CNTRL0_ERROR_TOLERANCE(6) |
AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0);
} else {
value |= AUDIO_CNTRL0_INJECT_NULLSMPL;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0);
/*
* Tegra30 and later use a slightly modified version of the register
* layout to accomodate for changes related to supporting HDA as the
* audio input source for HDMI. The source select field has moved to
* the SOR_AUDIO_CNTRL0 register, but the error tolerance and frames
* per block fields remain in the AUDIO_CNTRL0 register.
*/
if (hdmi->config->has_hda) {
/*
* Inject null samples into the audio FIFO for every frame in
* which the codec did not receive any samples. This applies
* to stereo LPCM only.
*
* XXX: This seems to be a remnant of MCP days when this was
* used to work around issues with monitors not being able to
* play back system startup sounds early. It is possibly not
* needed on Linux at all.
*/
if (hdmi->audio_channels == 2)
value = SOR_AUDIO_CNTRL0_INJECT_NULLSMPL;
else
value = 0;
value = AUDIO_CNTRL0_ERROR_TOLERANCE(6) |
AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0);
value |= source;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0);
}
config = tegra_hdmi_get_audio_config(hdmi->audio_freq, pclk);
/*
* On Tegra20, HDA is not a supported audio source and the source
* select field is part of the AUDIO_CNTRL0 register.
*/
value = AUDIO_CNTRL0_FRAMES_PER_BLOCK(0xc0) |
AUDIO_CNTRL0_ERROR_TOLERANCE(6);
if (!hdmi->config->has_hda)
value |= source;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_CNTRL0);
/*
* Advertise support for High Bit-Rate on Tegra114 and later.
*/
if (hdmi->config->has_hbr) {
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
value |= SOR_AUDIO_SPARE0_HBR_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
}
config = tegra_hdmi_get_audio_config(hdmi->audio_sample_rate,
hdmi->pixel_clock);
if (!config) {
dev_err(hdmi->dev, "cannot set audio to %u at %u pclk\n",
hdmi->audio_freq, pclk);
dev_err(hdmi->dev,
"cannot set audio to %u Hz at %u Hz pixel clock\n",
hdmi->audio_sample_rate, hdmi->pixel_clock);
return -EINVAL;
}
@ -526,8 +606,8 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
tegra_hdmi_writel(hdmi, ACR_SUBPACK_N(config->n) | ACR_ENABLE,
HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH);
value = ACR_SUBPACK_CTS(config->cts);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW);
tegra_hdmi_writel(hdmi, ACR_SUBPACK_CTS(config->cts),
HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW);
value = SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_SPARE);
@ -536,45 +616,55 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
value &= ~AUDIO_N_RESETF;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_AUDIO_N);
if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) {
switch (hdmi->audio_freq) {
case 32000:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320;
break;
case 44100:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441;
break;
case 48000:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480;
break;
case 88200:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882;
break;
case 96000:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960;
break;
case 176400:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764;
break;
case 192000:
offset = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920;
break;
}
tegra_hdmi_writel(hdmi, config->aval, offset);
}
if (hdmi->config->has_hda)
tegra_hdmi_write_aval(hdmi, config->aval);
tegra_hdmi_setup_audio_fs_tables(hdmi);
return 0;
}
static void tegra_hdmi_disable_audio(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value &= ~GENERIC_CTRL_AUDIO;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
}
static void tegra_hdmi_enable_audio(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value |= GENERIC_CTRL_AUDIO;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
}
static void tegra_hdmi_write_eld(struct tegra_hdmi *hdmi)
{
size_t length = drm_eld_size(hdmi->output.connector.eld), i;
u32 value;
for (i = 0; i < length; i++)
tegra_hdmi_writel(hdmi, i << 8 | hdmi->output.connector.eld[i],
HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR);
/*
* The HDA codec will always report an ELD buffer size of 96 bytes and
* the HDA codec driver will check that each byte read from the buffer
* is valid. Therefore every byte must be written, even if no 96 bytes
* were parsed from EDID.
*/
for (i = length; i < HDMI_ELD_BUFFER_SIZE; i++)
tegra_hdmi_writel(hdmi, i << 8 | 0,
HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR);
value = SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE);
}
static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size)
{
u32 value = 0;
@ -644,12 +734,6 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
u8 buffer[17];
ssize_t err;
if (hdmi->dvi) {
tegra_hdmi_writel(hdmi, 0,
HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
return;
}
err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
if (err < 0) {
dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err);
@ -663,9 +747,24 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
}
tegra_hdmi_write_infopack(hdmi, buffer, err);
}
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
static void tegra_hdmi_disable_avi_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
value &= ~INFOFRAME_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
}
static void tegra_hdmi_enable_avi_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
value |= INFOFRAME_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
}
static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
@ -674,12 +773,6 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
u8 buffer[14];
ssize_t err;
if (hdmi->dvi) {
tegra_hdmi_writel(hdmi, 0,
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
return;
}
err = hdmi_audio_infoframe_init(&frame);
if (err < 0) {
dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n",
@ -687,7 +780,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
return;
}
frame.channels = 2;
frame.channels = hdmi->audio_channels;
err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
if (err < 0) {
@ -703,9 +796,24 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
* bytes can be programmed.
*/
tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err));
}
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
static void tegra_hdmi_disable_audio_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
value &= ~INFOFRAME_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
}
static void tegra_hdmi_enable_audio_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
value |= INFOFRAME_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
}
static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
@ -713,14 +821,6 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
struct hdmi_vendor_infoframe frame;
u8 buffer[10];
ssize_t err;
u32 value;
if (!hdmi->stereo) {
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value &= ~GENERIC_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
return;
}
hdmi_vendor_infoframe_init(&frame);
frame.s3d_struct = HDMI_3D_STRUCTURE_FRAME_PACKING;
@ -733,6 +833,20 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
}
tegra_hdmi_write_infopack(hdmi, buffer, err);
}
static void tegra_hdmi_disable_stereo_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value &= ~GENERIC_CTRL_ENABLE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
}
static void tegra_hdmi_enable_stereo_infoframe(struct tegra_hdmi *hdmi)
{
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
value |= GENERIC_CTRL_ENABLE;
@ -772,10 +886,25 @@ static bool tegra_output_is_hdmi(struct tegra_output *output)
return drm_detect_hdmi_monitor(edid);
}
static enum drm_connector_status
tegra_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
struct tegra_output *output = connector_to_output(connector);
struct tegra_hdmi *hdmi = to_hdmi(output);
enum drm_connector_status status;
status = tegra_output_connector_detect(connector, force);
if (status == connector_status_connected)
return status;
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE);
return status;
}
static const struct drm_connector_funcs tegra_hdmi_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.detect = tegra_output_connector_detect,
.detect = tegra_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_output_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@ -814,7 +943,9 @@ static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
struct tegra_hdmi *hdmi = to_hdmi(output);
u32 value;
/*
@ -828,6 +959,20 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
tegra_dc_commit(dc);
}
if (!hdmi->dvi) {
if (hdmi->stereo)
tegra_hdmi_disable_stereo_infoframe(hdmi);
tegra_hdmi_disable_audio_infoframe(hdmi);
tegra_hdmi_disable_avi_infoframe(hdmi);
tegra_hdmi_disable_audio(hdmi);
}
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE);
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK);
pm_runtime_put(hdmi->dev);
}
static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
@ -836,21 +981,28 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
struct device_node *node = output->dev->of_node;
struct tegra_hdmi *hdmi = to_hdmi(output);
unsigned int pulse_start, div82, pclk;
unsigned int pulse_start, div82;
int retries = 1000;
u32 value;
int err;
hdmi->dvi = !tegra_output_is_hdmi(output);
pm_runtime_get_sync(hdmi->dev);
pclk = mode->clock * 1000;
/*
* Enable and unmask the HDA codec SCRATCH0 register interrupt. This
* is used for interoperability between the HDA codec driver and the
* HDMI driver.
*/
tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_ENABLE);
tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_MASK);
hdmi->pixel_clock = mode->clock * 1000;
h_sync_width = mode->hsync_end - mode->hsync_start;
h_back_porch = mode->htotal - mode->hsync_end;
h_front_porch = mode->hsync_start - mode->hdisplay;
err = clk_set_rate(hdmi->clk, pclk);
err = clk_set_rate(hdmi->clk, hdmi->pixel_clock);
if (err < 0) {
dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n",
err);
@ -909,17 +1061,15 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_REFCLK);
hdmi->dvi = !tegra_output_is_hdmi(output);
if (!hdmi->dvi) {
err = tegra_hdmi_setup_audio(hdmi, pclk);
err = tegra_hdmi_setup_audio(hdmi);
if (err < 0)
hdmi->dvi = true;
}
if (of_device_is_compatible(node, "nvidia,tegra20-hdmi")) {
/*
* TODO: add ELD support
*/
}
if (hdmi->config->has_hda)
tegra_hdmi_write_eld(hdmi);
rekey = HDMI_REKEY_DEFAULT;
value = HDMI_CTRL_REKEY(rekey);
@ -931,20 +1081,17 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_CTRL);
if (hdmi->dvi)
tegra_hdmi_writel(hdmi, 0x0,
HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
else
tegra_hdmi_writel(hdmi, GENERIC_CTRL_AUDIO,
HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
if (!hdmi->dvi) {
tegra_hdmi_setup_avi_infoframe(hdmi, mode);
tegra_hdmi_setup_audio_infoframe(hdmi);
if (hdmi->stereo)
tegra_hdmi_setup_stereo_infoframe(hdmi);
}
/* TMDS CONFIG */
for (i = 0; i < hdmi->config->num_tmds; i++) {
if (pclk <= hdmi->config->tmds[i].pclk) {
if (hdmi->pixel_clock <= hdmi->config->tmds[i].pclk) {
tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]);
break;
}
@ -1031,6 +1178,15 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
tegra_dc_commit(dc);
if (!hdmi->dvi) {
tegra_hdmi_enable_avi_infoframe(hdmi);
tegra_hdmi_enable_audio_infoframe(hdmi);
tegra_hdmi_enable_audio(hdmi);
if (hdmi->stereo)
tegra_hdmi_enable_stereo_infoframe(hdmi);
}
/* TODO: add HDCP support */
}
@ -1235,8 +1391,14 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
DUMP_REG(HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG);
DUMP_REG(HDMI_NV_PDISP_KEY_SKEY_INDEX);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_SPARE0);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE);
DUMP_REG(HDMI_NV_PDISP_INT_STATUS);
DUMP_REG(HDMI_NV_PDISP_INT_MASK);
DUMP_REG(HDMI_NV_PDISP_INT_ENABLE);
DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT);
#undef DUMP_REG
@ -1360,14 +1522,6 @@ static int tegra_hdmi_init(struct host1x_client *client)
return err;
}
err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
return err;
}
reset_control_deassert(hdmi->rst);
return 0;
}
@ -1377,9 +1531,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
tegra_output_exit(&hdmi->output);
reset_control_assert(hdmi->rst);
clk_disable_unprepare(hdmi->clk);
regulator_disable(hdmi->vdd);
regulator_disable(hdmi->pll);
regulator_disable(hdmi->hdmi);
@ -1401,6 +1552,8 @@ static const struct tegra_hdmi_config tegra20_hdmi_config = {
.fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = false,
.has_hda = false,
.has_hbr = false,
};
static const struct tegra_hdmi_config tegra30_hdmi_config = {
@ -1409,6 +1562,8 @@ static const struct tegra_hdmi_config tegra30_hdmi_config = {
.fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = false,
.has_hda = true,
.has_hbr = false,
};
static const struct tegra_hdmi_config tegra114_hdmi_config = {
@ -1417,6 +1572,8 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = {
.fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = true,
.has_hda = true,
.has_hbr = true,
};
static const struct tegra_hdmi_config tegra124_hdmi_config = {
@ -1425,6 +1582,8 @@ static const struct tegra_hdmi_config tegra124_hdmi_config = {
.fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = true,
.has_hda = true,
.has_hbr = true,
};
static const struct of_device_id tegra_hdmi_of_match[] = {
@ -1436,6 +1595,67 @@ static const struct of_device_id tegra_hdmi_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_hdmi_of_match);
static void hda_format_parse(unsigned int format, unsigned int *rate,
unsigned int *channels)
{
unsigned int mul, div;
if (format & AC_FMT_BASE_44K)
*rate = 44100;
else
*rate = 48000;
mul = (format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT;
div = (format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT;
*rate = *rate * (mul + 1) / (div + 1);
*channels = (format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT;
}
static irqreturn_t tegra_hdmi_irq(int irq, void *data)
{
struct tegra_hdmi *hdmi = data;
u32 value;
int err;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_INT_STATUS);
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_INT_STATUS);
if (value & INT_CODEC_SCRATCH0) {
unsigned int format;
u32 value;
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0);
if (value & SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID) {
unsigned int sample_rate, channels;
format = value & SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK;
hda_format_parse(format, &sample_rate, &channels);
hdmi->audio_sample_rate = sample_rate;
hdmi->audio_channels = channels;
err = tegra_hdmi_setup_audio(hdmi);
if (err < 0) {
tegra_hdmi_disable_audio_infoframe(hdmi);
tegra_hdmi_disable_audio(hdmi);
} else {
tegra_hdmi_setup_audio_infoframe(hdmi);
tegra_hdmi_enable_audio_infoframe(hdmi);
tegra_hdmi_enable_audio(hdmi);
}
} else {
tegra_hdmi_disable_audio_infoframe(hdmi);
tegra_hdmi_disable_audio(hdmi);
}
}
return IRQ_HANDLED;
}
static int tegra_hdmi_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@ -1453,8 +1673,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->config = match->data;
hdmi->dev = &pdev->dev;
hdmi->audio_source = AUTO;
hdmi->audio_freq = 44100;
hdmi->audio_sample_rate = 48000;
hdmi->audio_channels = 2;
hdmi->stereo = false;
hdmi->dvi = false;
@ -1515,6 +1737,17 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->irq = err;
err = devm_request_irq(hdmi->dev, hdmi->irq, tegra_hdmi_irq, 0,
dev_name(hdmi->dev), hdmi);
if (err < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
hdmi->irq, err);
return err;
}
platform_set_drvdata(pdev, hdmi);
pm_runtime_enable(&pdev->dev);
INIT_LIST_HEAD(&hdmi->client.list);
hdmi->client.ops = &hdmi_client_ops;
hdmi->client.dev = &pdev->dev;
@ -1526,8 +1759,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return err;
}
platform_set_drvdata(pdev, hdmi);
return 0;
}
@ -1536,6 +1767,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
int err;
pm_runtime_disable(&pdev->dev);
err = host1x_client_unregister(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@ -1545,17 +1778,61 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
tegra_output_remove(&hdmi->output);
clk_disable_unprepare(hdmi->clk_parent);
return 0;
}
#ifdef CONFIG_PM
static int tegra_hdmi_suspend(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
int err;
err = reset_control_assert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
usleep_range(1000, 2000);
clk_disable_unprepare(hdmi->clk);
return 0;
}
static int tegra_hdmi_resume(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
return err;
}
usleep_range(1000, 2000);
err = reset_control_deassert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
clk_disable_unprepare(hdmi->clk);
return err;
}
return 0;
}
#endif
static const struct dev_pm_ops tegra_hdmi_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_hdmi_suspend, tegra_hdmi_resume, NULL)
};
struct platform_driver tegra_hdmi_driver = {
.driver = {
.name = "tegra-hdmi",
.owner = THIS_MODULE,
.of_match_table = tegra_hdmi_of_match,
.pm = &tegra_hdmi_pm_ops,
},
.probe = tegra_hdmi_probe,
.remove = tegra_hdmi_remove,

View File

@ -468,9 +468,20 @@
#define HDMI_NV_PDISP_KEY_SKEY_INDEX 0xa3
#define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac
#define AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29)
#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20)
#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20)
#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20)
#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29)
#define HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0xae
#define SOR_AUDIO_SPARE0_HBR_ENABLE (1 << 27)
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0xba
#define SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID (1 << 30)
#define SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK 0xffff
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH1 0xbb
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR 0xbc
#define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd
#define SOR_AUDIO_HDA_PRESENSE_VALID (1 << 1)
#define SOR_AUDIO_HDA_PRESENSE_PRESENT (1 << 0)
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320 0xbf
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441 0xc0
@ -481,6 +492,14 @@
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 0xc5
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0xc5
#define HDMI_NV_PDISP_INT_STATUS 0xcc
#define INT_SCRATCH (1 << 3)
#define INT_CP_REQUEST (1 << 2)
#define INT_CODEC_SCRATCH1 (1 << 1)
#define INT_CODEC_SCRATCH0 (1 << 0)
#define HDMI_NV_PDISP_INT_MASK 0xcd
#define HDMI_NV_PDISP_INT_ENABLE 0xce
#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1
#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0)
#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8)

View File

@ -36,6 +36,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
if (edid) {
err = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
kfree(edid);
}

View File

@ -7,11 +7,13 @@
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@ -149,6 +151,8 @@ struct tegra_sor_soc {
const struct tegra_sor_hdmi_settings *settings;
unsigned int num_settings;
const u8 *xbar_cfg;
};
struct tegra_sor;
@ -169,7 +173,9 @@ struct tegra_sor {
struct reset_control *rst;
struct clk *clk_parent;
struct clk *clk_brick;
struct clk *clk_safe;
struct clk *clk_src;
struct clk *clk_dp;
struct clk *clk;
@ -190,6 +196,18 @@ struct tegra_sor {
struct regulator *hdmi_supply;
};
struct tegra_sor_state {
struct drm_connector_state base;
unsigned int bpc;
};
static inline struct tegra_sor_state *
to_sor_state(struct drm_connector_state *state)
{
return container_of(state, struct tegra_sor_state, base);
}
struct tegra_sor_config {
u32 bits_per_pixel;
@ -225,6 +243,118 @@ static inline void tegra_sor_writel(struct tegra_sor *sor, u32 value,
writel(value, sor->regs + (offset << 2));
}
static int tegra_sor_set_parent_clock(struct tegra_sor *sor, struct clk *parent)
{
int err;
clk_disable_unprepare(sor->clk);
err = clk_set_parent(sor->clk, parent);
if (err < 0)
return err;
err = clk_prepare_enable(sor->clk);
if (err < 0)
return err;
return 0;
}
struct tegra_clk_sor_brick {
struct clk_hw hw;
struct tegra_sor *sor;
};
static inline struct tegra_clk_sor_brick *to_brick(struct clk_hw *hw)
{
return container_of(hw, struct tegra_clk_sor_brick, hw);
}
static const char * const tegra_clk_sor_brick_parents[] = {
"pll_d2_out0", "pll_dp"
};
static int tegra_clk_sor_brick_set_parent(struct clk_hw *hw, u8 index)
{
struct tegra_clk_sor_brick *brick = to_brick(hw);
struct tegra_sor *sor = brick->sor;
u32 value;
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
switch (index) {
case 0:
value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK;
break;
case 1:
value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
break;
}
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
return 0;
}
static u8 tegra_clk_sor_brick_get_parent(struct clk_hw *hw)
{
struct tegra_clk_sor_brick *brick = to_brick(hw);
struct tegra_sor *sor = brick->sor;
u8 parent = U8_MAX;
u32 value;
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
switch (value & SOR_CLK_CNTRL_DP_CLK_SEL_MASK) {
case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK:
case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK:
parent = 0;
break;
case SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK:
case SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK:
parent = 1;
break;
}
return parent;
}
static const struct clk_ops tegra_clk_sor_brick_ops = {
.set_parent = tegra_clk_sor_brick_set_parent,
.get_parent = tegra_clk_sor_brick_get_parent,
};
static struct clk *tegra_clk_sor_brick_register(struct tegra_sor *sor,
const char *name)
{
struct tegra_clk_sor_brick *brick;
struct clk_init_data init;
struct clk *clk;
brick = devm_kzalloc(sor->dev, sizeof(*brick), GFP_KERNEL);
if (!brick)
return ERR_PTR(-ENOMEM);
brick->sor = sor;
init.name = name;
init.flags = 0;
init.parent_names = tegra_clk_sor_brick_parents;
init.num_parents = ARRAY_SIZE(tegra_clk_sor_brick_parents);
init.ops = &tegra_clk_sor_brick_ops;
brick->hw.init = &init;
clk = devm_clk_register(sor->dev, &brick->hw);
if (IS_ERR(clk))
kfree(brick);
return clk;
}
static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
struct drm_dp_link *link)
{
@ -569,7 +699,7 @@ static int tegra_sor_compute_params(struct tegra_sor *sor,
return false;
}
static int tegra_sor_calc_config(struct tegra_sor *sor,
static int tegra_sor_compute_config(struct tegra_sor *sor,
const struct drm_display_mode *mode,
struct tegra_sor_config *config,
struct drm_dp_link *link)
@ -661,6 +791,135 @@ static int tegra_sor_calc_config(struct tegra_sor *sor,
return 0;
}
static void tegra_sor_apply_config(struct tegra_sor *sor,
const struct tegra_sor_config *config)
{
u32 value;
value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
value |= SOR_DP_LINKCTL_TU_SIZE(config->tu_size);
tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
value = tegra_sor_readl(sor, SOR_DP_CONFIG0);
value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
value |= SOR_DP_CONFIG_WATERMARK(config->watermark);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config->active_count);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config->active_frac);
if (config->active_polarity)
value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
else
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
tegra_sor_writel(sor, value, SOR_DP_CONFIG0);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
value |= config->hblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
value |= config->vblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
}
static void tegra_sor_mode_set(struct tegra_sor *sor,
const struct drm_display_mode *mode,
struct tegra_sor_state *state)
{
struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc);
unsigned int vbe, vse, hbe, hse, vbs, hbs;
u32 value;
value = tegra_sor_readl(sor, SOR_STATE1);
value &= ~SOR_STATE_ASY_PIXELDEPTH_MASK;
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
value &= ~SOR_STATE_ASY_OWNER_MASK;
value |= SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
value &= ~SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
value |= SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
value &= ~SOR_STATE_ASY_VSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
value |= SOR_STATE_ASY_VSYNCPOL;
switch (state->bpc) {
case 16:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_48_444;
break;
case 12:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_36_444;
break;
case 10:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_30_444;
break;
case 8:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
break;
case 6:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
break;
default:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
break;
}
tegra_sor_writel(sor, value, SOR_STATE1);
/*
* TODO: The video timing programming below doesn't seem to match the
* register definitions.
*/
value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe));
/* sync end = sync width - 1 */
vse = mode->vsync_end - mode->vsync_start - 1;
hse = mode->hsync_end - mode->hsync_start - 1;
value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe));
/* blank end = sync end + back porch */
vbe = vse + (mode->vtotal - mode->vsync_end);
hbe = hse + (mode->htotal - mode->hsync_end);
value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe));
/* blank start = blank end + active */
vbs = vbe + mode->vdisplay;
hbs = hbe + mode->hdisplay;
value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe));
/* XXX interlacing support */
tegra_sor_writel(sor, 0x001, SOR_HEAD_STATE5(dc->pipe));
}
static int tegra_sor_detach(struct tegra_sor *sor)
{
unsigned long value, timeout;
@ -733,7 +992,8 @@ static int tegra_sor_power_down(struct tegra_sor *sor)
if ((value & SOR_PWR_TRIGGER) != 0)
return -ETIMEDOUT;
err = clk_set_parent(sor->clk, sor->clk_safe);
/* switch to safe parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
if (err < 0)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
@ -1038,6 +1298,22 @@ static void tegra_sor_debugfs_exit(struct tegra_sor *sor)
sor->debugfs = NULL;
}
static void tegra_sor_connector_reset(struct drm_connector *connector)
{
struct tegra_sor_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return;
if (connector->state) {
__drm_atomic_helper_connector_destroy_state(connector->state);
kfree(connector->state);
}
__drm_atomic_helper_connector_reset(connector, &state->base);
}
static enum drm_connector_status
tegra_sor_connector_detect(struct drm_connector *connector, bool force)
{
@ -1050,13 +1326,28 @@ tegra_sor_connector_detect(struct drm_connector *connector, bool force)
return tegra_output_connector_detect(connector, force);
}
static struct drm_connector_state *
tegra_sor_connector_duplicate_state(struct drm_connector *connector)
{
struct tegra_sor_state *state = to_sor_state(connector->state);
struct tegra_sor_state *copy;
copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
if (!copy)
return NULL;
__drm_atomic_helper_connector_duplicate_state(connector, &copy->base);
return &copy->base;
}
static const struct drm_connector_funcs tegra_sor_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset,
.reset = tegra_sor_connector_reset,
.detect = tegra_sor_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = tegra_output_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_duplicate_state = tegra_sor_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
@ -1081,6 +1372,10 @@ static enum drm_mode_status
tegra_sor_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
/* HDMI 2.0 modes are not yet supported */
if (mode->clock > 340000)
return MODE_NOCLOCK;
return MODE_OK;
}
@ -1140,8 +1435,7 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder)
if (output->panel)
drm_panel_unprepare(output->panel);
reset_control_assert(sor->rst);
clk_disable_unprepare(sor->clk);
pm_runtime_put(sor->dev);
}
#if 0
@ -1191,19 +1485,18 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
struct tegra_sor *sor = to_sor(output);
struct tegra_sor_config config;
struct tegra_sor_state *state;
struct drm_dp_link link;
u8 rate, lanes;
unsigned int i;
int err = 0;
u32 value;
err = clk_prepare_enable(sor->clk);
if (err < 0)
dev_err(sor->dev, "failed to enable clock: %d\n", err);
state = to_sor_state(output->connector.state);
reset_control_deassert(sor->rst);
pm_runtime_get_sync(sor->dev);
if (output->panel)
drm_panel_prepare(output->panel);
@ -1218,17 +1511,17 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
return;
}
err = clk_set_parent(sor->clk, sor->clk_safe);
/* switch to safe parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
if (err < 0)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
memset(&config, 0, sizeof(config));
config.bits_per_pixel = output->connector.display_info.bpc * 3;
config.bits_per_pixel = state->bpc * 3;
err = tegra_sor_calc_config(sor, mode, &config, &link);
err = tegra_sor_compute_config(sor, mode, &config, &link);
if (err < 0)
dev_err(sor->dev, "failed to compute link configuration: %d\n",
err);
dev_err(sor->dev, "failed to compute configuration: %d\n", err);
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
@ -1325,10 +1618,18 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
value &= ~SOR_PLL2_PORT_POWERDOWN;
tegra_sor_writel(sor, value, SOR_PLL2);
/* switch to DP clock */
err = clk_set_parent(sor->clk, sor->clk_dp);
/* XXX not in TRM */
for (value = 0, i = 0; i < 5; i++)
value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
SOR_XBAR_CTRL_LINK1_XSEL(i, i);
tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
/* switch to DP parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_dp);
if (err < 0)
dev_err(sor->dev, "failed to set DP parent clock: %d\n", err);
dev_err(sor->dev, "failed to set parent clock: %d\n", err);
/* power DP lanes */
value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
@ -1374,13 +1675,11 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
value |= drm_dp_link_rate_to_bw_code(link.rate) << 2;
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
/* set linkctl */
tegra_sor_apply_config(sor, &config);
/* enable link */
value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
value |= SOR_DP_LINKCTL_ENABLE;
value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size);
value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
@ -1393,35 +1692,6 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
tegra_sor_writel(sor, value, SOR_DP_TPG);
value = tegra_sor_readl(sor, SOR_DP_CONFIG0);
value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
value |= SOR_DP_CONFIG_WATERMARK(config.watermark);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac);
if (config.active_polarity)
value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
else
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
tegra_sor_writel(sor, value, SOR_DP_CONFIG0);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
value |= config.hblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
value |= config.vblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
/* enable pad calibration logic */
value = tegra_sor_readl(sor, SOR_DP_PADCTL0);
value |= SOR_DP_PADCTL_PAD_CAL_PD;
@ -1477,75 +1747,19 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to power up SOR: %d\n", err);
/*
* configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete
* raster, associate with display controller)
*/
value = SOR_STATE_ASY_PROTOCOL_DP_A |
SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
value &= ~SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
value |= SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
value &= ~SOR_STATE_ASY_VSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
value |= SOR_STATE_ASY_VSYNCPOL;
switch (config.bits_per_pixel) {
case 24:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
break;
case 18:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
break;
default:
BUG();
break;
}
tegra_sor_writel(sor, value, SOR_STATE1);
/*
* TODO: The video timing programming below doesn't seem to match the
* register definitions.
*/
value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe));
vse = mode->vsync_end - mode->vsync_start - 1;
hse = mode->hsync_end - mode->hsync_start - 1;
value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe));
vbe = vse + (mode->vsync_start - mode->vdisplay);
hbe = hse + (mode->hsync_start - mode->hdisplay);
value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe));
vbs = vbe + mode->vdisplay;
hbs = hbe + mode->hdisplay;
value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe));
tegra_sor_writel(sor, 0x1, SOR_HEAD_STATE5(dc->pipe));
/* CSTM (LVDS, link A/B, upper) */
value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
SOR_CSTM_UPPER;
tegra_sor_writel(sor, value, SOR_CSTM);
/* use DP-A protocol */
value = tegra_sor_readl(sor, SOR_STATE1);
value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
value |= SOR_STATE_ASY_PROTOCOL_DP_A;
tegra_sor_writel(sor, value, SOR_STATE1);
tegra_sor_mode_set(sor, mode, state);
/* PWM setup */
err = tegra_sor_setup_pwm(sor, 250);
if (err < 0)
@ -1577,11 +1791,15 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_sor_state *state = to_sor_state(conn_state);
struct tegra_dc *dc = to_tegra_dc(conn_state->crtc);
unsigned long pclk = crtc_state->mode.clock * 1000;
struct tegra_sor *sor = to_sor(output);
struct drm_display_info *info;
int err;
info = &output->connector.display_info;
err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent,
pclk, 0);
if (err < 0) {
@ -1589,6 +1807,18 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
return err;
}
switch (info->bpc) {
case 8:
case 6:
state->bpc = info->bpc;
break;
default:
DRM_DEBUG_KMS("%u bits-per-color not supported\n", info->bpc);
state->bpc = 8;
break;
}
return 0;
}
@ -1751,9 +1981,7 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to power off HDMI rail: %d\n", err);
reset_control_assert(sor->rst);
usleep_range(1000, 2000);
clk_disable_unprepare(sor->clk);
pm_runtime_put(sor->dev);
}
static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
@ -1761,26 +1989,21 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
struct tegra_output *output = encoder_to_output(encoder);
unsigned int h_ref_to_sync = 1, pulse_start, max_ac;
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
unsigned int vbe, vse, hbe, hse, vbs, hbs, div;
struct tegra_sor_hdmi_settings *settings;
struct tegra_sor *sor = to_sor(output);
struct tegra_sor_state *state;
struct drm_display_mode *mode;
struct drm_display_info *info;
unsigned int div, i;
u32 value;
int err;
state = to_sor_state(output->connector.state);
mode = &encoder->crtc->state->adjusted_mode;
info = &output->connector.display_info;
err = clk_prepare_enable(sor->clk);
if (err < 0)
dev_err(sor->dev, "failed to enable clock: %d\n", err);
pm_runtime_get_sync(sor->dev);
usleep_range(1000, 2000);
reset_control_deassert(sor->rst);
err = clk_set_parent(sor->clk, sor->clk_safe);
/* switch to safe parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
if (err < 0)
dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
@ -1876,22 +2099,20 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div);
tegra_sor_writel(sor, value, SOR_REFCLK);
/* XXX don't hardcode */
value = SOR_XBAR_CTRL_LINK1_XSEL(4, 4) |
SOR_XBAR_CTRL_LINK1_XSEL(3, 3) |
SOR_XBAR_CTRL_LINK1_XSEL(2, 2) |
SOR_XBAR_CTRL_LINK1_XSEL(1, 1) |
SOR_XBAR_CTRL_LINK1_XSEL(0, 0) |
SOR_XBAR_CTRL_LINK0_XSEL(4, 4) |
SOR_XBAR_CTRL_LINK0_XSEL(3, 3) |
SOR_XBAR_CTRL_LINK0_XSEL(2, 0) |
SOR_XBAR_CTRL_LINK0_XSEL(1, 1) |
SOR_XBAR_CTRL_LINK0_XSEL(0, 2);
tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
/* XXX not in TRM */
for (value = 0, i = 0; i < 5; i++)
value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->soc->xbar_cfg[i]) |
SOR_XBAR_CTRL_LINK1_XSEL(i, i);
tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
err = clk_set_parent(sor->clk, sor->clk_parent);
/* switch to parent clock */
err = clk_set_parent(sor->clk_src, sor->clk_parent);
if (err < 0)
dev_err(sor->dev, "failed to set source clock: %d\n", err);
err = tegra_sor_set_parent_clock(sor, sor->clk_src);
if (err < 0)
dev_err(sor->dev, "failed to set parent clock: %d\n", err);
@ -2001,7 +2222,7 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
value &= ~DITHER_CONTROL_MASK;
value &= ~BASE_COLOR_SIZE_MASK;
switch (info->bpc) {
switch (state->bpc) {
case 6:
value |= BASE_COLOR_SIZE_666;
break;
@ -2011,7 +2232,8 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
break;
default:
WARN(1, "%u bits-per-color not supported\n", info->bpc);
WARN(1, "%u bits-per-color not supported\n", state->bpc);
value |= BASE_COLOR_SIZE_888;
break;
}
@ -2021,83 +2243,19 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to power up SOR: %d\n", err);
/* configure mode */
value = tegra_sor_readl(sor, SOR_STATE1);
value &= ~SOR_STATE_ASY_PIXELDEPTH_MASK;
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
value &= ~SOR_STATE_ASY_OWNER_MASK;
value |= SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
value &= ~SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
value |= SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
value &= ~SOR_STATE_ASY_VSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
value |= SOR_STATE_ASY_VSYNCPOL;
switch (info->bpc) {
case 8:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444;
break;
case 6:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444;
break;
default:
BUG();
break;
}
tegra_sor_writel(sor, value, SOR_STATE1);
/* configure dynamic range of output */
value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe));
value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK;
value &= ~SOR_HEAD_STATE_DYNRANGE_MASK;
tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe));
/* configure colorspace */
value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe));
value &= ~SOR_HEAD_STATE_COLORSPACE_MASK;
value |= SOR_HEAD_STATE_COLORSPACE_RGB;
tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe));
/*
* TODO: The video timing programming below doesn't seem to match the
* register definitions.
*/
value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe));
/* sync end = sync width - 1 */
vse = mode->vsync_end - mode->vsync_start - 1;
hse = mode->hsync_end - mode->hsync_start - 1;
value = ((vse & 0x7fff) << 16) | (hse & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe));
/* blank end = sync end + back porch */
vbe = vse + (mode->vtotal - mode->vsync_end);
hbe = hse + (mode->htotal - mode->hsync_end);
value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe));
/* blank start = blank end + active */
vbs = vbe + mode->vdisplay;
hbs = hbe + mode->hdisplay;
value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff);
tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe));
tegra_sor_writel(sor, 0x1, SOR_HEAD_STATE5(dc->pipe));
tegra_sor_mode_set(sor, mode, state);
tegra_sor_update(sor);
@ -2195,11 +2353,14 @@ static int tegra_sor_init(struct host1x_client *client)
* XXX: Remove this reset once proper hand-over from firmware to
* kernel is possible.
*/
if (sor->rst) {
err = reset_control_assert(sor->rst);
if (err < 0) {
dev_err(sor->dev, "failed to assert SOR reset: %d\n", err);
dev_err(sor->dev, "failed to assert SOR reset: %d\n",
err);
return err;
}
}
err = clk_prepare_enable(sor->clk);
if (err < 0) {
@ -2209,11 +2370,14 @@ static int tegra_sor_init(struct host1x_client *client)
usleep_range(1000, 3000);
if (sor->rst) {
err = reset_control_deassert(sor->rst);
if (err < 0) {
dev_err(sor->dev, "failed to deassert SOR reset: %d\n", err);
dev_err(sor->dev, "failed to deassert SOR reset: %d\n",
err);
return err;
}
}
err = clk_prepare_enable(sor->clk_safe);
if (err < 0)
@ -2323,11 +2487,16 @@ static const struct tegra_sor_ops tegra_sor_hdmi_ops = {
.remove = tegra_sor_hdmi_remove,
};
static const u8 tegra124_sor_xbar_cfg[5] = {
0, 1, 2, 3, 4
};
static const struct tegra_sor_soc tegra124_sor = {
.supports_edp = true,
.supports_lvds = true,
.supports_hdmi = false,
.supports_dp = false,
.xbar_cfg = tegra124_sor_xbar_cfg,
};
static const struct tegra_sor_soc tegra210_sor = {
@ -2335,6 +2504,11 @@ static const struct tegra_sor_soc tegra210_sor = {
.supports_lvds = false,
.supports_hdmi = false,
.supports_dp = false,
.xbar_cfg = tegra124_sor_xbar_cfg,
};
static const u8 tegra210_sor_xbar_cfg[5] = {
2, 1, 0, 3, 4
};
static const struct tegra_sor_soc tegra210_sor1 = {
@ -2345,6 +2519,8 @@ static const struct tegra_sor_soc tegra210_sor1 = {
.num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults),
.settings = tegra210_sor_hdmi_defaults,
.xbar_cfg = tegra210_sor_xbar_cfg,
};
static const struct of_device_id tegra_sor_of_match[] = {
@ -2434,12 +2610,15 @@ static int tegra_sor_probe(struct platform_device *pdev)
goto remove;
}
if (!pdev->dev.pm_domain) {
sor->rst = devm_reset_control_get(&pdev->dev, "sor");
if (IS_ERR(sor->rst)) {
err = PTR_ERR(sor->rst);
dev_err(&pdev->dev, "failed to get reset control: %d\n", err);
dev_err(&pdev->dev, "failed to get reset control: %d\n",
err);
goto remove;
}
}
sor->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sor->clk)) {
@ -2448,6 +2627,16 @@ static int tegra_sor_probe(struct platform_device *pdev)
goto remove;
}
if (sor->soc->supports_hdmi || sor->soc->supports_dp) {
sor->clk_src = devm_clk_get(&pdev->dev, "source");
if (IS_ERR(sor->clk_src)) {
err = PTR_ERR(sor->clk_src);
dev_err(sor->dev, "failed to get source clock: %d\n",
err);
goto remove;
}
}
sor->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(sor->clk_parent)) {
err = PTR_ERR(sor->clk_parent);
@ -2469,6 +2658,19 @@ static int tegra_sor_probe(struct platform_device *pdev)
goto remove;
}
platform_set_drvdata(pdev, sor);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
sor->clk_brick = tegra_clk_sor_brick_register(sor, "sor1_brick");
pm_runtime_put(&pdev->dev);
if (IS_ERR(sor->clk_brick)) {
err = PTR_ERR(sor->clk_brick);
dev_err(&pdev->dev, "failed to register SOR clock: %d\n", err);
goto remove;
}
INIT_LIST_HEAD(&sor->client.list);
sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev;
@ -2480,8 +2682,6 @@ static int tegra_sor_probe(struct platform_device *pdev)
goto remove;
}
platform_set_drvdata(pdev, sor);
return 0;
remove:
@ -2497,6 +2697,8 @@ static int tegra_sor_remove(struct platform_device *pdev)
struct tegra_sor *sor = platform_get_drvdata(pdev);
int err;
pm_runtime_disable(&pdev->dev);
err = host1x_client_unregister(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@ -2515,10 +2717,62 @@ static int tegra_sor_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int tegra_sor_suspend(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
if (sor->rst) {
err = reset_control_assert(sor->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
}
usleep_range(1000, 2000);
clk_disable_unprepare(sor->clk);
return 0;
}
static int tegra_sor_resume(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(sor->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
return err;
}
usleep_range(1000, 2000);
if (sor->rst) {
err = reset_control_deassert(sor->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
clk_disable_unprepare(sor->clk);
return err;
}
}
return 0;
}
#endif
static const struct dev_pm_ops tegra_sor_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_sor_suspend, tegra_sor_resume, NULL)
};
struct platform_driver tegra_sor_driver = {
.driver = {
.name = "tegra-sor",
.of_match_table = tegra_sor_of_match,
.pm = &tegra_sor_pm_ops,
},
.probe = tegra_sor_probe,
.remove = tegra_sor_remove,

View File

@ -27,6 +27,9 @@
#define SOR_STATE_ASY_PIXELDEPTH_MASK (0xf << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_18_444 (0x2 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 (0x5 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_30_444 (0x6 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_36_444 (0x8 << 17)
#define SOR_STATE_ASY_PIXELDEPTH_BPP_48_444 (0x9 << 17)
#define SOR_STATE_ASY_VSYNCPOL (1 << 13)
#define SOR_STATE_ASY_HSYNCPOL (1 << 12)
#define SOR_STATE_ASY_PROTOCOL_MASK (0xf << 8)

View File

@ -96,12 +96,12 @@ fail:
*/
static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
{
u32 pos = pb->pos;
u32 *p = (u32 *)((void *)pb->mapped + pos);
WARN_ON(pos == pb->fence);
u32 *p = (u32 *)((void *)pb->mapped + pb->pos);
WARN_ON(pb->pos == pb->fence);
*(p++) = op1;
*(p++) = op2;
pb->pos = (pos + 8) & (pb->size_bytes - 1);
pb->pos = (pb->pos + 8) & (pb->size_bytes - 1);
}
/*
@ -134,14 +134,19 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
enum cdma_event event)
{
for (;;) {
struct push_buffer *pb = &cdma->push_buffer;
unsigned int space;
if (event == CDMA_EVENT_SYNC_QUEUE_EMPTY)
switch (event) {
case CDMA_EVENT_SYNC_QUEUE_EMPTY:
space = list_empty(&cdma->sync_queue) ? 1 : 0;
else if (event == CDMA_EVENT_PUSH_BUFFER_SPACE) {
struct push_buffer *pb = &cdma->push_buffer;
break;
case CDMA_EVENT_PUSH_BUFFER_SPACE:
space = host1x_pushbuffer_space(pb);
} else {
break;
default:
WARN_ON(1);
return -EINVAL;
}
@ -159,12 +164,14 @@ unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
mutex_lock(&cdma->lock);
continue;
}
cdma->event = event;
mutex_unlock(&cdma->lock);
down(&cdma->sem);
mutex_lock(&cdma->lock);
}
return 0;
}
@ -234,6 +241,7 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
/* Start timer on next pending syncpt */
if (job->timeout)
cdma_start_timer_locked(cdma, job);
break;
}
@ -247,7 +255,9 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
/* Pop push buffer slots */
if (job->num_slots) {
struct push_buffer *pb = &cdma->push_buffer;
host1x_pushbuffer_pop(pb, job->num_slots);
if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE)
signal = true;
}
@ -269,11 +279,9 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
struct device *dev)
{
u32 restart_addr;
u32 syncpt_incrs;
struct host1x_job *job = NULL;
u32 syncpt_val;
struct host1x *host1x = cdma_to_host1x(cdma);
u32 restart_addr, syncpt_incrs, syncpt_val;
struct host1x_job *job = NULL;
syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
@ -342,9 +350,11 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
syncpt_val += syncpt_incrs;
}
/* The following sumbits from the same client may be dependent on the
/*
* The following sumbits from the same client may be dependent on the
* failed submit and therefore they may fail. Force a small timeout
* to make the queue cleanup faster */
* to make the queue cleanup faster.
*/
list_for_each_entry_from(job, &cdma->sync_queue, list)
if (job->client == cdma->timeout.client)
@ -375,6 +385,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma)
err = host1x_pushbuffer_init(&cdma->push_buffer);
if (err)
return err;
return 0;
}
@ -410,6 +421,7 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
/* init state on first submit with timeout value */
if (!cdma->timeout.initialized) {
int err;
err = host1x_hw_cdma_timeout_init(host1x, cdma,
job->syncpt_id);
if (err) {
@ -418,6 +430,7 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
}
}
}
if (!cdma->running)
host1x_hw_cdma_start(host1x, cdma);
@ -448,6 +461,7 @@ void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
slots_free = host1x_cdma_wait_locked(cdma,
CDMA_EVENT_PUSH_BUFFER_SPACE);
}
cdma->slots_free = slots_free - 1;
cdma->slots_used++;
host1x_pushbuffer_push(pb, op1, op2);

View File

@ -83,9 +83,10 @@ EXPORT_SYMBOL(host1x_channel_put);
struct host1x_channel *host1x_channel_request(struct device *dev)
{
struct host1x *host = dev_get_drvdata(dev->parent);
int max_channels = host->info->nb_channels;
unsigned int max_channels = host->info->nb_channels;
struct host1x_channel *channel = NULL;
int index, err;
unsigned long index;
int err;
mutex_lock(&host->chlist_mutex);

View File

@ -39,6 +39,7 @@ void host1x_debug_output(struct output *o, const char *fmt, ...)
va_start(args, fmt);
len = vsnprintf(o->buf, sizeof(o->buf), fmt, args);
va_end(args);
o->fn(o->ctx, o->buf, len);
}
@ -48,13 +49,17 @@ static int show_channels(struct host1x_channel *ch, void *data, bool show_fifo)
struct output *o = data;
mutex_lock(&ch->reflock);
if (ch->refcount) {
mutex_lock(&ch->cdma.lock);
if (show_fifo)
host1x_hw_show_channel_fifo(m, ch, o);
host1x_hw_show_channel_cdma(m, ch, o);
mutex_unlock(&ch->cdma.lock);
}
mutex_unlock(&ch->reflock);
return 0;
@ -62,22 +67,27 @@ static int show_channels(struct host1x_channel *ch, void *data, bool show_fifo)
static void show_syncpts(struct host1x *m, struct output *o)
{
int i;
unsigned int i;
host1x_debug_output(o, "---- syncpts ----\n");
for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
u32 max = host1x_syncpt_read_max(m->syncpt + i);
u32 min = host1x_syncpt_load(m->syncpt + i);
if (!min && !max)
continue;
host1x_debug_output(o, "id %d (%s) min %d max %d\n",
host1x_debug_output(o, "id %u (%s) min %d max %d\n",
i, m->syncpt[i].name, min, max);
}
for (i = 0; i < host1x_syncpt_nb_bases(m); i++) {
u32 base_val;
base_val = host1x_syncpt_load_wait_base(m->syncpt + i);
if (base_val)
host1x_debug_output(o, "waitbase id %d val %d\n", i,
host1x_debug_output(o, "waitbase id %u val %d\n", i,
base_val);
}
@ -114,7 +124,9 @@ static int host1x_debug_show_all(struct seq_file *s, void *unused)
.fn = write_to_seqfile,
.ctx = s
};
show_all(s->private, &o);
return 0;
}
@ -124,7 +136,9 @@ static int host1x_debug_show(struct seq_file *s, void *unused)
.fn = write_to_seqfile,
.ctx = s
};
show_all_no_fifo(s->private, &o);
return 0;
}
@ -201,6 +215,7 @@ void host1x_debug_dump(struct host1x *host1x)
struct output o = {
.fn = write_to_printk
};
show_all(host1x, &o);
}
@ -209,5 +224,6 @@ void host1x_debug_dump_syncpts(struct host1x *host1x)
struct output o = {
.fn = write_to_printk
};
show_syncpts(host1x, &o);
}

View File

@ -102,7 +102,7 @@ static const struct host1x_info host1x05_info = {
.dma_mask = DMA_BIT_MASK(34),
};
static struct of_device_id host1x_of_match[] = {
static const struct of_device_id host1x_of_match[] = {
{ .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, },
{ .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, },
{ .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },

View File

@ -45,7 +45,7 @@ struct host1x_cdma_ops {
void (*start)(struct host1x_cdma *cdma);
void (*stop)(struct host1x_cdma *cdma);
void (*flush)(struct host1x_cdma *cdma);
int (*timeout_init)(struct host1x_cdma *cdma, u32 syncpt_id);
int (*timeout_init)(struct host1x_cdma *cdma, unsigned int syncpt);
void (*timeout_destroy)(struct host1x_cdma *cdma);
void (*freeze)(struct host1x_cdma *cdma);
void (*resume)(struct host1x_cdma *cdma, u32 getptr);
@ -82,20 +82,20 @@ struct host1x_intr_ops {
int (*init_host_sync)(struct host1x *host, u32 cpm,
void (*syncpt_thresh_work)(struct work_struct *work));
void (*set_syncpt_threshold)(
struct host1x *host, u32 id, u32 thresh);
void (*enable_syncpt_intr)(struct host1x *host, u32 id);
void (*disable_syncpt_intr)(struct host1x *host, u32 id);
struct host1x *host, unsigned int id, u32 thresh);
void (*enable_syncpt_intr)(struct host1x *host, unsigned int id);
void (*disable_syncpt_intr)(struct host1x *host, unsigned int id);
void (*disable_all_syncpt_intrs)(struct host1x *host);
int (*free_syncpt_irq)(struct host1x *host);
};
struct host1x_info {
int nb_channels; /* host1x: num channels supported */
int nb_pts; /* host1x: num syncpoints supported */
int nb_bases; /* host1x: num syncpoints supported */
int nb_mlocks; /* host1x: number of mlocks */
int (*init)(struct host1x *); /* initialize per SoC ops */
int sync_offset;
unsigned int nb_channels; /* host1x: number of channels supported */
unsigned int nb_pts; /* host1x: number of syncpoints supported */
unsigned int nb_bases; /* host1x: number of syncpoint bases supported */
unsigned int nb_mlocks; /* host1x: number of mlocks supported */
int (*init)(struct host1x *host1x); /* initialize per SoC ops */
unsigned int sync_offset; /* offset of syncpoint registers */
u64 dma_mask; /* mask of addressable memory */
};
@ -109,7 +109,6 @@ struct host1x {
struct clk *clk;
struct mutex intr_mutex;
struct workqueue_struct *intr_wq;
int intr_syncpt_irq;
const struct host1x_syncpt_ops *syncpt_op;
@ -183,19 +182,20 @@ static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm,
}
static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host,
u32 id, u32 thresh)
unsigned int id,
u32 thresh)
{
host->intr_op->set_syncpt_threshold(host, id, thresh);
}
static inline void host1x_hw_intr_enable_syncpt_intr(struct host1x *host,
u32 id)
unsigned int id)
{
host->intr_op->enable_syncpt_intr(host, id);
}
static inline void host1x_hw_intr_disable_syncpt_intr(struct host1x *host,
u32 id)
unsigned int id)
{
host->intr_op->disable_syncpt_intr(host, id);
}
@ -212,9 +212,9 @@ static inline int host1x_hw_intr_free_syncpt_irq(struct host1x *host)
static inline int host1x_hw_channel_init(struct host1x *host,
struct host1x_channel *channel,
int chid)
unsigned int id)
{
return host->channel_op->init(channel, host, chid);
return host->channel_op->init(channel, host, id);
}
static inline int host1x_hw_channel_submit(struct host1x *host,
@ -243,9 +243,9 @@ static inline void host1x_hw_cdma_flush(struct host1x *host,
static inline int host1x_hw_cdma_timeout_init(struct host1x *host,
struct host1x_cdma *cdma,
u32 syncpt_id)
unsigned int syncpt)
{
return host->cdma_op->timeout_init(cdma, syncpt_id);
return host->cdma_op->timeout_init(cdma, syncpt);
}
static inline void host1x_hw_cdma_timeout_destroy(struct host1x *host,

View File

@ -41,7 +41,7 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
{
struct host1x *host1x = cdma_to_host1x(cdma);
struct push_buffer *pb = &cdma->push_buffer;
u32 i;
unsigned int i;
for (i = 0; i < syncpt_incrs; i++)
host1x_syncpt_incr(cdma->timeout.syncpt);
@ -58,6 +58,7 @@ static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
&pb->phys, getptr);
getptr = (getptr + 8) & (pb->size_bytes - 1);
}
wmb();
}
@ -162,12 +163,14 @@ static void cdma_stop(struct host1x_cdma *cdma)
struct host1x_channel *ch = cdma_to_channel(cdma);
mutex_lock(&cdma->lock);
if (cdma->running) {
host1x_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY);
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
HOST1X_CHANNEL_DMACTRL);
cdma->running = false;
}
mutex_unlock(&cdma->lock);
}
@ -213,11 +216,11 @@ static void cdma_resume(struct host1x_cdma *cdma, u32 getptr)
u32 cmdproc_stop;
dev_dbg(host1x->dev,
"resuming channel (id %d, DMAGET restart = 0x%x)\n",
"resuming channel (id %u, DMAGET restart = 0x%x)\n",
ch->id, getptr);
cmdproc_stop = host1x_sync_readl(host1x, HOST1X_SYNC_CMDPROC_STOP);
cmdproc_stop &= ~(BIT(ch->id));
cmdproc_stop &= ~BIT(ch->id);
host1x_sync_writel(host1x, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
cdma->torndown = false;
@ -231,14 +234,11 @@ static void cdma_resume(struct host1x_cdma *cdma, u32 getptr)
*/
static void cdma_timeout_handler(struct work_struct *work)
{
u32 prev_cmdproc, cmdproc_stop, syncpt_val;
struct host1x_cdma *cdma;
struct host1x *host1x;
struct host1x_channel *ch;
u32 syncpt_val;
u32 prev_cmdproc, cmdproc_stop;
cdma = container_of(to_delayed_work(work), struct host1x_cdma,
timeout.wq);
host1x = cdma_to_host1x(cdma);
@ -277,7 +277,7 @@ static void cdma_timeout_handler(struct work_struct *work)
return;
}
dev_warn(host1x->dev, "%s: timeout: %d (%s), HW thresh %d, done %d\n",
dev_warn(host1x->dev, "%s: timeout: %u (%s), HW thresh %d, done %d\n",
__func__, cdma->timeout.syncpt->id, cdma->timeout.syncpt->name,
syncpt_val, cdma->timeout.syncpt_val);
@ -291,7 +291,7 @@ static void cdma_timeout_handler(struct work_struct *work)
/*
* Init timeout resources
*/
static int cdma_timeout_init(struct host1x_cdma *cdma, u32 syncpt_id)
static int cdma_timeout_init(struct host1x_cdma *cdma, unsigned int syncpt)
{
INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler);
cdma->timeout.initialized = true;
@ -306,6 +306,7 @@ static void cdma_timeout_destroy(struct host1x_cdma *cdma)
{
if (cdma->timeout.initialized)
cancel_delayed_work(&cdma->timeout.wq);
cdma->timeout.initialized = false;
}

View File

@ -46,6 +46,7 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
*/
for (i = 0; i < words; i += TRACE_MAX_LENGTH) {
u32 num_words = min(words - i, TRACE_MAX_LENGTH);
offset += i * sizeof(u32);
trace_host1x_cdma_push_gather(dev_name(dev), bo,
@ -66,6 +67,7 @@ static void submit_gathers(struct host1x_job *job)
struct host1x_job_gather *g = &job->gathers[i];
u32 op1 = host1x_opcode_gather(g->words);
u32 op2 = g->base + g->offset;
trace_write_gather(cdma, g->bo, g->offset, op1 & 0xffff);
host1x_cdma_push(cdma, op1, op2);
}
@ -75,7 +77,8 @@ static inline void synchronize_syncpt_base(struct host1x_job *job)
{
struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
struct host1x_syncpt *sp = host->syncpt + job->syncpt_id;
u32 id, value;
unsigned int id;
u32 value;
value = host1x_syncpt_read_max(sp);
id = sp->base->id;

View File

@ -40,8 +40,7 @@ enum {
static unsigned int show_channel_command(struct output *o, u32 val)
{
unsigned mask;
unsigned subop;
unsigned int mask, subop;
switch (val >> 28) {
case HOST1X_OPCODE_SETCLASS:
@ -51,12 +50,11 @@ static unsigned int show_channel_command(struct output *o, u32 val)
val >> 6 & 0x3ff,
val >> 16 & 0xfff, mask);
return hweight8(mask);
} else {
host1x_debug_output(o, "SETCL(class=%03x)\n",
val >> 6 & 0x3ff);
return 0;
}
host1x_debug_output(o, "SETCL(class=%03x)\n", val >> 6 & 0x3ff);
return 0;
case HOST1X_OPCODE_INCR:
host1x_debug_output(o, "INCR(offset=%03x, [",
val >> 16 & 0xfff);
@ -143,7 +141,8 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma)
struct host1x_job *job;
list_for_each_entry(job, &cdma->sync_queue, list) {
int i;
unsigned int i;
host1x_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d, first_get=%08x, timeout=%d num_slots=%d, num_handles=%d\n",
job, job->syncpt_id, job->syncpt_end,
job->first_get, job->timeout,
@ -190,7 +189,7 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
cbread = host1x_sync_readl(host, HOST1X_SYNC_CBREAD(ch->id));
cbstat = host1x_sync_readl(host, HOST1X_SYNC_CBSTAT(ch->id));
host1x_debug_output(o, "%d-%s: ", ch->id, dev_name(ch->dev));
host1x_debug_output(o, "%u-%s: ", ch->id, dev_name(ch->dev));
if (HOST1X_CHANNEL_DMACTRL_DMASTOP_V(dmactrl) ||
!ch->cdma.push_buffer.mapped) {
@ -207,7 +206,6 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
HOST1X_CLASS_HOST1X &&
HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat) ==
HOST1X_UCLASS_WAIT_SYNCPT_BASE) {
base = (cbread >> 16) & 0xff;
baseval =
host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(base));
@ -236,7 +234,7 @@ static void host1x_debug_show_channel_fifo(struct host1x *host,
u32 val, rd_ptr, wr_ptr, start, end;
unsigned int data_count = 0;
host1x_debug_output(o, "%d: fifo:\n", ch->id);
host1x_debug_output(o, "%u: fifo:\n", ch->id);
val = host1x_ch_readl(ch, HOST1X_CHANNEL_FIFOSTAT);
host1x_debug_output(o, "FIFOSTAT %08x\n", val);
@ -290,20 +288,22 @@ static void host1x_debug_show_channel_fifo(struct host1x *host,
static void host1x_debug_show_mlocks(struct host1x *host, struct output *o)
{
int i;
unsigned int i;
host1x_debug_output(o, "---- mlocks ----\n");
for (i = 0; i < host1x_syncpt_nb_mlocks(host); i++) {
u32 owner =
host1x_sync_readl(host, HOST1X_SYNC_MLOCK_OWNER(i));
if (HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(owner))
host1x_debug_output(o, "%d: locked by channel %d\n",
host1x_debug_output(o, "%u: locked by channel %u\n",
i, HOST1X_SYNC_MLOCK_OWNER_CHID_V(owner));
else if (HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(owner))
host1x_debug_output(o, "%d: locked by cpu\n", i);
host1x_debug_output(o, "%u: locked by cpu\n", i);
else
host1x_debug_output(o, "%d: unlocked\n", i);
host1x_debug_output(o, "%u: unlocked\n", i);
}
host1x_debug_output(o, "\n");
}

View File

@ -38,14 +38,14 @@ static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt)
host1x_sync_writel(host, BIT_MASK(id),
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(BIT_WORD(id)));
queue_work(host->intr_wq, &syncpt->intr.work);
schedule_work(&syncpt->intr.work);
}
static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
{
struct host1x *host = dev_id;
unsigned long reg;
int i, id;
unsigned int i, id;
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
reg = host1x_sync_readl(host,
@ -62,7 +62,7 @@ static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
{
u32 i;
unsigned int i;
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
host1x_sync_writel(host, 0xffffffffu,
@ -72,10 +72,12 @@ static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
}
}
static int _host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
static int
_host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
void (*syncpt_thresh_work)(struct work_struct *))
{
int i, err;
unsigned int i;
int err;
host1x_hw_intr_disable_all_syncpt_intrs(host);
@ -106,18 +108,21 @@ static int _host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
}
static void _host1x_intr_set_syncpt_threshold(struct host1x *host,
u32 id, u32 thresh)
unsigned int id,
u32 thresh)
{
host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
}
static void _host1x_intr_enable_syncpt_intr(struct host1x *host, u32 id)
static void _host1x_intr_enable_syncpt_intr(struct host1x *host,
unsigned int id)
{
host1x_sync_writel(host, BIT_MASK(id),
HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(BIT_WORD(id)));
}
static void _host1x_intr_disable_syncpt_intr(struct host1x *host, u32 id)
static void _host1x_intr_disable_syncpt_intr(struct host1x *host,
unsigned int id)
{
host1x_sync_writel(host, BIT_MASK(id),
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(BIT_WORD(id)));
@ -127,8 +132,13 @@ static void _host1x_intr_disable_syncpt_intr(struct host1x *host, u32 id)
static int _host1x_free_syncpt_irq(struct host1x *host)
{
unsigned int i;
devm_free_irq(host->dev, host->intr_syncpt_irq, host);
flush_workqueue(host->intr_wq);
for (i = 0; i < host->info->nb_pts; i++)
cancel_work_sync(&host->syncpt[i].intr.work);
return 0;
}

View File

@ -26,8 +26,9 @@
*/
static void syncpt_restore(struct host1x_syncpt *sp)
{
u32 min = host1x_syncpt_read_min(sp);
struct host1x *host = sp->host;
int min = host1x_syncpt_read_min(sp);
host1x_sync_writel(host, min, HOST1X_SYNC_SYNCPT(sp->id));
}
@ -37,6 +38,7 @@ static void syncpt_restore(struct host1x_syncpt *sp)
static void syncpt_restore_wait_base(struct host1x_syncpt *sp)
{
struct host1x *host = sp->host;
host1x_sync_writel(host, sp->base_val,
HOST1X_SYNC_SYNCPT_BASE(sp->id));
}
@ -47,6 +49,7 @@ static void syncpt_restore_wait_base(struct host1x_syncpt *sp)
static void syncpt_read_wait_base(struct host1x_syncpt *sp)
{
struct host1x *host = sp->host;
sp->base_val =
host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(sp->id));
}
@ -85,6 +88,7 @@ static int syncpt_cpu_incr(struct host1x_syncpt *sp)
if (!host1x_syncpt_client_managed(sp) &&
host1x_syncpt_idle(sp))
return -EINVAL;
host1x_sync_writel(host, BIT_MASK(sp->id),
HOST1X_SYNC_SYNCPT_CPU_INCR(reg_offset));
wmb();
@ -95,10 +99,10 @@ static int syncpt_cpu_incr(struct host1x_syncpt *sp)
/* remove a wait pointed to by patch_addr */
static int syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
{
u32 override = host1x_class_host_wait_syncpt(
HOST1X_SYNCPT_RESERVED, 0);
u32 override = host1x_class_host_wait_syncpt(HOST1X_SYNCPT_RESERVED, 0);
*((u32 *)patch_addr) = override;
return 0;
}

View File

@ -122,18 +122,20 @@ static void action_submit_complete(struct host1x_waitlist *waiter)
static void action_wakeup(struct host1x_waitlist *waiter)
{
wait_queue_head_t *wq = waiter->data;
wake_up(wq);
}
static void action_wakeup_interruptible(struct host1x_waitlist *waiter)
{
wait_queue_head_t *wq = waiter->data;
wake_up_interruptible(wq);
}
typedef void (*action_handler)(struct host1x_waitlist *waiter);
static action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = {
static const action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = {
action_submit_complete,
action_wakeup,
action_wakeup_interruptible,
@ -209,7 +211,7 @@ static void syncpt_thresh_work(struct work_struct *work)
host1x_syncpt_load(host->syncpt + id));
}
int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
int host1x_intr_add_action(struct host1x *host, unsigned int id, u32 thresh,
enum host1x_intr_action action, void *data,
struct host1x_waitlist *waiter, void **ref)
{
@ -254,7 +256,7 @@ int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
return 0;
}
void host1x_intr_put_ref(struct host1x *host, u32 id, void *ref)
void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref)
{
struct host1x_waitlist *waiter = ref;
struct host1x_syncpt *syncpt;
@ -277,9 +279,6 @@ int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
mutex_init(&host->intr_mutex);
host->intr_syncpt_irq = irq_sync;
host->intr_wq = create_workqueue("host_syncpt");
if (!host->intr_wq)
return -ENOMEM;
for (id = 0; id < nb_pts; ++id) {
struct host1x_syncpt *syncpt = host->syncpt + id;
@ -288,7 +287,7 @@ int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
INIT_LIST_HEAD(&syncpt->intr.wait_head);
snprintf(syncpt->intr.thresh_irq_name,
sizeof(syncpt->intr.thresh_irq_name),
"host1x_sp_%02d", id);
"host1x_sp_%02u", id);
}
host1x_intr_start(host);
@ -299,7 +298,6 @@ int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
void host1x_intr_deinit(struct host1x *host)
{
host1x_intr_stop(host);
destroy_workqueue(host->intr_wq);
}
void host1x_intr_start(struct host1x *host)
@ -342,7 +340,7 @@ void host1x_intr_stop(struct host1x *host)
if (!list_empty(&syncpt[id].intr.wait_head)) {
/* output diagnostics */
mutex_unlock(&host->intr_mutex);
pr_warn("%s cannot stop syncpt intr id=%d\n",
pr_warn("%s cannot stop syncpt intr id=%u\n",
__func__, id);
return;
}

View File

@ -75,7 +75,7 @@ struct host1x_waitlist {
*
* This is a non-blocking api.
*/
int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
int host1x_intr_add_action(struct host1x *host, unsigned int id, u32 thresh,
enum host1x_intr_action action, void *data,
struct host1x_waitlist *waiter, void **ref);
@ -84,7 +84,7 @@ int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
* You must call this if you passed non-NULL as ref.
* @ref the ref returned from host1x_intr_add_action()
*/
void host1x_intr_put_ref(struct host1x *host, u32 id, void *ref);
void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref);
/* Initialize host1x sync point interrupt */
int host1x_intr_init(struct host1x *host, unsigned int irq_sync);

View File

@ -161,7 +161,7 @@ static int do_waitchks(struct host1x_job *job, struct host1x *host,
if (host1x_syncpt_is_expired(sp, wait->thresh)) {
dev_dbg(host->dev,
"drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n",
"drop WAIT id %u (%s) thresh 0x%x, min 0x%x\n",
wait->syncpt_id, sp->name, wait->thresh,
host1x_syncpt_read_min(sp));
@ -464,6 +464,7 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
for (i = 0; i < job->num_gathers; i++) {
struct host1x_job_gather *g = &job->gathers[i];
size += g->words * sizeof(u32);
}
@ -514,6 +515,7 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)
bitmap_zero(waitchk_mask, host1x_syncpt_nb_pts(host));
for (i = 0; i < job->num_waitchk; i++) {
u32 syncpt_id = job->waitchk[i].syncpt_id;
if (syncpt_id < host1x_syncpt_nb_pts(host))
set_bit(syncpt_id, waitchk_mask);
}
@ -571,9 +573,11 @@ void host1x_job_unpin(struct host1x_job *job)
for (i = 0; i < job->num_unpins; i++) {
struct host1x_job_unpin_data *unpin = &job->unpins[i];
host1x_bo_unpin(unpin->bo, unpin->sgt);
host1x_bo_put(unpin->bo);
}
job->num_unpins = 0;
if (job->gather_copy_size)

View File

@ -73,7 +73,7 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
return NULL;
}
name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id,
dev ? dev_name(dev) : NULL);
if (!name)
return NULL;
@ -110,12 +110,14 @@ EXPORT_SYMBOL(host1x_syncpt_incr_max);
void host1x_syncpt_restore(struct host1x *host)
{
struct host1x_syncpt *sp_base = host->syncpt;
u32 i;
unsigned int i;
for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
host1x_hw_syncpt_restore(host, sp_base + i);
for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
wmb();
}
@ -126,7 +128,7 @@ void host1x_syncpt_restore(struct host1x *host)
void host1x_syncpt_save(struct host1x *host)
{
struct host1x_syncpt *sp_base = host->syncpt;
u32 i;
unsigned int i;
for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
if (host1x_syncpt_client_managed(sp_base + i))
@ -146,6 +148,7 @@ void host1x_syncpt_save(struct host1x *host)
u32 host1x_syncpt_load(struct host1x_syncpt *sp)
{
u32 val;
val = host1x_hw_syncpt_load(sp->host, sp);
trace_host1x_syncpt_load_min(sp->id, val);
@ -157,10 +160,9 @@ u32 host1x_syncpt_load(struct host1x_syncpt *sp)
*/
u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
{
u32 val;
host1x_hw_syncpt_load_wait_base(sp->host, sp);
val = sp->base_val;
return val;
return sp->base_val;
}
/*
@ -179,6 +181,7 @@ EXPORT_SYMBOL(host1x_syncpt_incr);
static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
{
host1x_hw_syncpt_load(sp->host, sp);
return host1x_syncpt_is_expired(sp, thresh);
}
@ -201,6 +204,7 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
if (host1x_syncpt_is_expired(sp, thresh)) {
if (value)
*value = host1x_syncpt_load(sp);
return 0;
}
@ -209,6 +213,7 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
if (host1x_syncpt_is_expired(sp, thresh)) {
if (value)
*value = val;
goto done;
}
@ -239,32 +244,42 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
/* wait for the syncpoint, or timeout, or signal */
while (timeout) {
long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout);
int remain = wait_event_interruptible_timeout(wq,
int remain;
remain = wait_event_interruptible_timeout(wq,
syncpt_load_min_is_expired(sp, thresh),
check);
if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) {
if (value)
*value = host1x_syncpt_load(sp);
err = 0;
break;
}
if (remain < 0) {
err = remain;
break;
}
timeout -= check;
if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) {
dev_warn(sp->host->dev,
"%s: syncpoint id %d (%s) stuck waiting %d, timeout=%ld\n",
"%s: syncpoint id %u (%s) stuck waiting %d, timeout=%ld\n",
current->comm, sp->id, sp->name,
thresh, timeout);
host1x_debug_dump_syncpts(sp->host);
if (check_count == MAX_STUCK_CHECK_COUNT)
host1x_debug_dump(sp->host);
check_count++;
}
}
host1x_intr_put_ref(sp->host, sp->id, ref);
done:
@ -279,7 +294,9 @@ bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
{
u32 current_val;
u32 future_val;
smp_rmb();
current_val = (u32)atomic_read(&sp->min_val);
future_val = (u32)atomic_read(&sp->max_val);
@ -341,14 +358,14 @@ int host1x_syncpt_init(struct host1x *host)
{
struct host1x_syncpt_base *bases;
struct host1x_syncpt *syncpt;
int i;
unsigned int i;
syncpt = devm_kzalloc(host->dev, sizeof(*syncpt) * host->info->nb_pts,
syncpt = devm_kcalloc(host->dev, host->info->nb_pts, sizeof(*syncpt),
GFP_KERNEL);
if (!syncpt)
return -ENOMEM;
bases = devm_kzalloc(host->dev, sizeof(*bases) * host->info->nb_bases,
bases = devm_kcalloc(host->dev, host->info->nb_bases, sizeof(*bases),
GFP_KERNEL);
if (!bases)
return -ENOMEM;
@ -378,6 +395,7 @@ struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
unsigned long flags)
{
struct host1x *host = dev_get_drvdata(dev->parent);
return host1x_syncpt_alloc(host, dev, flags);
}
EXPORT_SYMBOL(host1x_syncpt_request);
@ -398,8 +416,9 @@ EXPORT_SYMBOL(host1x_syncpt_free);
void host1x_syncpt_deinit(struct host1x *host)
{
int i;
struct host1x_syncpt *sp = host->syncpt;
unsigned int i;
for (i = 0; i < host->info->nb_pts; i++, sp++)
kfree(sp->name);
}
@ -407,10 +426,11 @@ void host1x_syncpt_deinit(struct host1x *host)
/*
* Read max. It indicates how many operations there are in queue, either in
* channel or in a software thread.
* */
*/
u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
{
smp_rmb();
return (u32)atomic_read(&sp->max_val);
}
EXPORT_SYMBOL(host1x_syncpt_read_max);
@ -421,6 +441,7 @@ EXPORT_SYMBOL(host1x_syncpt_read_max);
u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
{
smp_rmb();
return (u32)atomic_read(&sp->min_val);
}
EXPORT_SYMBOL(host1x_syncpt_read_min);
@ -431,25 +452,26 @@ u32 host1x_syncpt_read(struct host1x_syncpt *sp)
}
EXPORT_SYMBOL(host1x_syncpt_read);
int host1x_syncpt_nb_pts(struct host1x *host)
unsigned int host1x_syncpt_nb_pts(struct host1x *host)
{
return host->info->nb_pts;
}
int host1x_syncpt_nb_bases(struct host1x *host)
unsigned int host1x_syncpt_nb_bases(struct host1x *host)
{
return host->info->nb_bases;
}
int host1x_syncpt_nb_mlocks(struct host1x *host)
unsigned int host1x_syncpt_nb_mlocks(struct host1x *host)
{
return host->info->nb_mlocks;
}
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id)
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id)
{
if (host->info->nb_pts < id)
return NULL;
return host->syncpt + id;
}
EXPORT_SYMBOL(host1x_syncpt_get);

View File

@ -37,7 +37,7 @@ struct host1x_syncpt_base {
};
struct host1x_syncpt {
int id;
unsigned int id;
atomic_t min_val;
atomic_t max_val;
u32 base_val;
@ -58,13 +58,13 @@ int host1x_syncpt_init(struct host1x *host);
void host1x_syncpt_deinit(struct host1x *host);
/* Return number of sync point supported. */
int host1x_syncpt_nb_pts(struct host1x *host);
unsigned int host1x_syncpt_nb_pts(struct host1x *host);
/* Return number of wait bases supported. */
int host1x_syncpt_nb_bases(struct host1x *host);
unsigned int host1x_syncpt_nb_bases(struct host1x *host);
/* Return number of mlocks supported. */
int host1x_syncpt_nb_mlocks(struct host1x *host);
unsigned int host1x_syncpt_nb_mlocks(struct host1x *host);
/*
* Check sync point sanity. If max is larger than min, there have too many

View File

@ -391,4 +391,12 @@ exit:
}
EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map);
void pinconf_generic_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map,
unsigned num_maps)
{
pinctrl_utils_free_map(pctldev, map, num_maps);
}
EXPORT_SYMBOL_GPL(pinconf_generic_dt_free_map);
#endif

View File

@ -175,6 +175,8 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev,
int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np_config, struct pinctrl_map **map,
unsigned *num_maps, enum pinctrl_map_type type);
void pinconf_generic_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned num_maps);
static inline int pinconf_generic_dt_node_to_map_group(
struct pinctrl_dev *pctldev, struct device_node *np_config,