Merge tag 'drm-misc-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-misc into drm-next

- cleanups&fixes for dw-hdmi bride driver (Laurent)
- updates for adv bridge driver (John Stultz) for nexus
- drm_crtc_from_index helper rollout (Shawn Guo)
- removing drm_framebuffer_unregister_private from drivers&core
- target_vblank (Andrey Grodzovsky)
- misc tiny stuff

* tag 'drm-misc-next-2017-01-23' of git://anongit.freedesktop.org/git/drm-misc: (49 commits)
  drm: qxl: Open code teardown function for qxl
  drm: qxl: Open code probing sequence for qxl
  drm/bridge: adv7511: Re-write the i2c address before EDID probing
  drm/bridge: adv7511: Reuse __adv7511_power_on/off() when probing EDID
  drm/bridge: adv7511: Rework adv7511_power_on/off() so they can be reused internally
  drm/bridge: adv7511: Enable HPD interrupts to support hotplug and improve monitor detection
  drm/bridge: adv7511: Switch to using drm_kms_helper_hotplug_event()
  drm/bridge: adv7511: Use work_struct to defer hotplug handing to out of irq context
  drm: vc4: use crtc helper drm_crtc_from_index()
  drm: tegra: use crtc helper drm_crtc_from_index()
  drm: nouveau: use crtc helper drm_crtc_from_index()
  drm: mediatek: use crtc helper drm_crtc_from_index()
  drm: kirin: use crtc helper drm_crtc_from_index()
  drm: exynos: use crtc helper drm_crtc_from_index()
  dt-bindings: display: dw-hdmi: Clean up DT bindings documentation
  drm: bridge: dw-hdmi: Assert SVSRET before resetting the PHY
  drm: bridge: dw-hdmi: Fix the name of the PHY reset macros
  drm: bridge: dw-hdmi: Define and use macros for PHY register addresses
  drm: bridge: dw-hdmi: Detect PHY type at runtime
  drm: bridge: dw-hdmi: Handle overflow workaround based on device version
  ...
This commit is contained in:
Dave Airlie 2017-01-27 12:10:24 +10:00
commit 3875623c56
47 changed files with 926 additions and 493 deletions

View File

@ -38,10 +38,22 @@ The following input format properties are required except in "rgb 1x" and
- adi,input-justification: The input bit justification ("left", "evenly",
"right").
- avdd-supply: A 1.8V supply that powers up the AVDD pin on the chip.
- dvdd-supply: A 1.8V supply that powers up the DVDD pin on the chip.
- pvdd-supply: A 1.8V supply that powers up the PVDD pin on the chip.
- dvdd-3v-supply: A 3.3V supply that powers up the pin called DVDD_3V
on the chip.
- bgvdd-supply: A 1.8V supply that powers up the BGVDD pin. This is
needed only for ADV7511.
The following properties are required for ADV7533:
- adi,dsi-lanes: Number of DSI data lanes connected to the DSI host. It should
be one of 1, 2, 3 or 4.
- a2vdd-supply: 1.8V supply that powers up the A2VDD pin on the chip.
- v3p3-supply: A 3.3V supply that powers up the V3P3 pin on the chip.
- v1p2-supply: A supply that powers up the V1P2 pin on the chip. It can be
either 1.2V or 1.8V.
Optional properties:

View File

@ -1,52 +1,33 @@
DesignWare HDMI bridge bindings
Synopsys DesignWare HDMI TX Encoder
===================================
Required properties:
- compatible: platform specific such as:
* "snps,dw-hdmi-tx"
* "fsl,imx6q-hdmi"
* "fsl,imx6dl-hdmi"
* "rockchip,rk3288-dw-hdmi"
- reg: Physical base address and length of the controller's registers.
- interrupts: The HDMI interrupt number
- clocks, clock-names : must have the phandles to the HDMI iahb and isfr clocks,
as described in Documentation/devicetree/bindings/clock/clock-bindings.txt,
the clocks are soc specific, the clock-names should be "iahb", "isfr"
-port@[X]: SoC specific port nodes with endpoint definitions as defined
in Documentation/devicetree/bindings/media/video-interfaces.txt,
please refer to the SoC specific binding document:
* Documentation/devicetree/bindings/display/imx/hdmi.txt
* Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
This document defines device tree properties for the Synopsys DesignWare HDMI
TX Encoder (DWC HDMI TX). It doesn't constitue a device tree binding
specification by itself but is meant to be referenced by platform-specific
device tree bindings.
Optional properties
- reg-io-width: the width of the reg:1,4, default set to 1 if not present
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing,
if the property is omitted, a functionally reduced I2C bus
controller on DW HDMI is probed
- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
When referenced from platform device tree bindings the properties defined in
this document are defined as follows. The platform device tree bindings are
responsible for defining whether each property is required or optional.
Example:
hdmi: hdmi@0120000 {
compatible = "fsl,imx6q-hdmi";
reg = <0x00120000 0x9000>;
interrupts = <0 115 0x04>;
gpr = <&gpr>;
clocks = <&clks 123>, <&clks 124>;
clock-names = "iahb", "isfr";
ddc-i2c-bus = <&i2c2>;
- reg: Memory mapped base address and length of the DWC HDMI TX registers.
port@0 {
reg = <0>;
- reg-io-width: Width of the registers specified by the reg property. The
value is expressed in bytes and must be equal to 1 or 4 if specified. The
register width defaults to 1 if the property is not present.
hdmi_mux_0: endpoint {
remote-endpoint = <&ipu1_di0_hdmi>;
};
};
- interrupts: Reference to the DWC HDMI TX interrupt.
port@1 {
reg = <1>;
- clocks: References to all the clocks specified in the clock-names property
as specified in Documentation/devicetree/bindings/clock/clock-bindings.txt.
hdmi_mux_1: endpoint {
remote-endpoint = <&ipu1_di1_hdmi>;
};
};
};
- clock-names: The DWC HDMI TX uses the following clocks.
- "iahb" is the bus clock for either AHB and APB (mandatory).
- "isfr" is the internal register configuration clock (mandatory).
- "cec" is the HDMI CEC controller main clock (optional).
- ports: The connectivity of the DWC HDMI TX with the rest of the system is
expressed in using ports as specified in the device graph bindings defined
in Documentation/devicetree/bindings/graph.txt. The numbering of the ports
is platform-specific.

View File

@ -1,29 +1,36 @@
Device-Tree bindings for HDMI Transmitter
Freescale i.MX6 DWC HDMI TX Encoder
===================================
HDMI Transmitter
================
The HDMI transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
with a companion PHY IP.
These DT bindings follow the Synopsys DWC HDMI TX bindings defined in
Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt with the
following device-specific properties.
The HDMI Transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
with accompanying PHY IP.
Required properties:
- #address-cells : should be <1>
- #size-cells : should be <0>
- compatible : should be "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi".
- gpr : should be <&gpr>.
The phandle points to the iomuxc-gpr region containing the HDMI
multiplexer control register.
- clocks, clock-names : phandles to the HDMI iahb and isrf clocks, as described
in Documentation/devicetree/bindings/clock/clock-bindings.txt and
Documentation/devicetree/bindings/clock/imx6q-clock.txt.
- port@[0-4]: Up to four port nodes with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt,
corresponding to the four inputs to the HDMI multiplexer.
Optional properties:
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
- compatible : Shall be one of "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi".
- reg: See dw_hdmi.txt.
- interrupts: HDMI interrupt number
- clocks: See dw_hdmi.txt.
- clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt.
- ports: See dw_hdmi.txt. The DWC HDMI shall have between one and four ports,
numbered 0 to 3, corresponding to the four inputs of the HDMI multiplexer.
Each port shall have a single endpoint.
- gpr : Shall contain a phandle to the iomuxc-gpr region containing the HDMI
multiplexer control register.
example:
Optional properties
- ddc-i2c-bus: The HDMI DDC bus can be connected to either a system I2C master
or the functionally-reduced I2C master contained in the DWC HDMI. When
connected to a system I2C master this property contains a phandle to that
I2C master controller.
Example:
gpr: iomuxc-gpr@020e0000 {
/* ... */

View File

@ -1,24 +1,39 @@
Rockchip specific extensions to the Synopsys Designware HDMI
================================
Rockchip DWC HDMI TX Encoder
============================
The HDMI transmitter is a Synopsys DesignWare HDMI 1.4 TX controller IP
with a companion PHY IP.
These DT bindings follow the Synopsys DWC HDMI TX bindings defined in
Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt with the
following device-specific properties.
Required properties:
- compatible: "rockchip,rk3288-dw-hdmi";
- reg: Physical base address and length of the controller's registers.
- clocks: phandle to hdmi iahb and isfr clocks.
- clock-names: should be "iahb" "isfr"
- rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
- compatible: Shall contain "rockchip,rk3288-dw-hdmi".
- reg: See dw_hdmi.txt.
- reg-io-width: See dw_hdmi.txt. Shall be 4.
- interrupts: HDMI interrupt number
- ports: contain a port node with endpoint definitions as defined in
Documentation/devicetree/bindings/media/video-interfaces.txt. For
vopb,set the reg = <0> and set the reg = <1> for vopl.
- reg-io-width: the width of the reg:1,4, the value should be 4 on
rk3288 platform
- clocks: See dw_hdmi.txt.
- clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt.
- ports: See dw_hdmi.txt. The DWC HDMI shall have a single port numbered 0
corresponding to the video input of the controller. The port shall have two
endpoints, numbered 0 and 1, connected respectively to the vopb and vopl.
- rockchip,grf: Shall reference the GRF to mux vopl/vopb.
Optional properties
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
- ddc-i2c-bus: The HDMI DDC bus can be connected to either a system I2C master
or the functionally-reduced I2C master contained in the DWC HDMI. When
connected to a system I2C master this property contains a phandle to that
I2C master controller.
- clock-names: See dw_hdmi.txt. The "cec" clock is optional.
- clock-names: May contain "cec" as defined in dw_hdmi.txt.
Example:
hdmi: hdmi@ff980000 {
compatible = "rockchip,rk3288-dw-hdmi";
reg = <0xff980000 0x20000>;

View File

@ -470,3 +470,9 @@ DRM MM Range Allocator Function References
.. kernel-doc:: include/drm/drm_mm.h
:internal:
DRM Cache Handling
==================
.. kernel-doc:: drivers/gpu/drm/drm_cache.c
:export:

View File

@ -138,7 +138,7 @@ config DRM_KMS_CMA_HELPER
config DRM_VM
bool
depends on DRM
depends on DRM && MMU
source "drivers/gpu/drm/i2c/Kconfig"
@ -267,7 +267,7 @@ source "drivers/gpu/drm/meson/Kconfig"
menuconfig DRM_LEGACY
bool "Enable legacy drivers (DANGEROUS)"
depends on DRM
depends on DRM && MMU
select DRM_VM
help
Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous

View File

@ -1,6 +1,6 @@
config DRM_ARMADA
tristate "DRM support for Marvell Armada SoCs"
depends on DRM && HAVE_CLK && ARM
depends on DRM && HAVE_CLK && ARM && MMU
select DRM_KMS_HELPER
help
Support the "LCD" controllers found on the Marvell Armada 510

View File

@ -12,6 +12,7 @@
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_mipi_dsi.h>
@ -317,6 +318,8 @@ struct adv7511 {
bool edid_read;
wait_queue_head_t wq;
struct work_struct hpd_work;
struct drm_bridge bridge;
struct drm_connector connector;
@ -329,6 +332,9 @@ struct adv7511 {
struct gpio_desc *gpio_pd;
struct regulator_bulk_data *supplies;
unsigned int num_supplies;
/* ADV7533 DSI RX related params */
struct device_node *host_node;
struct mipi_dsi_device *dsi;

View File

@ -325,7 +325,7 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
}
static void adv7511_power_on(struct adv7511 *adv7511)
static void __adv7511_power_on(struct adv7511 *adv7511)
{
adv7511->current_edid_segment = -1;
@ -338,7 +338,7 @@ static void adv7511_power_on(struct adv7511 *adv7511)
* Still, let's be safe and stick to the documentation.
*/
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
ADV7511_INT0_EDID_READY);
ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
ADV7511_INT1_DDC_ERROR);
}
@ -354,6 +354,11 @@ static void adv7511_power_on(struct adv7511 *adv7511)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
ADV7511_REG_POWER2_HPD_SRC_MASK,
ADV7511_REG_POWER2_HPD_SRC_NONE);
}
static void adv7511_power_on(struct adv7511 *adv7511)
{
__adv7511_power_on(adv7511);
/*
* Most of the registers are reset during power down or when HPD is low.
@ -362,21 +367,23 @@ static void adv7511_power_on(struct adv7511 *adv7511)
if (adv7511->type == ADV7533)
adv7533_dsi_power_on(adv7511);
adv7511->powered = true;
}
static void adv7511_power_off(struct adv7511 *adv7511)
static void __adv7511_power_off(struct adv7511 *adv7511)
{
/* TODO: setup additional power down modes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN);
regcache_mark_dirty(adv7511->regmap);
}
static void adv7511_power_off(struct adv7511 *adv7511)
{
__adv7511_power_off(adv7511);
if (adv7511->type == ADV7533)
adv7533_dsi_power_off(adv7511);
adv7511->powered = false;
}
@ -402,6 +409,27 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
return false;
}
static void adv7511_hpd_work(struct work_struct *work)
{
struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work);
enum drm_connector_status status;
unsigned int val;
int ret;
ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val);
if (ret < 0)
status = connector_status_disconnected;
else if (val & ADV7511_STATUS_HPD)
status = connector_status_connected;
else
status = connector_status_disconnected;
if (adv7511->connector.status != status) {
adv7511->connector.status = status;
drm_kms_helper_hotplug_event(adv7511->connector.dev);
}
}
static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
{
unsigned int irq0, irq1;
@ -419,7 +447,7 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder)
drm_helper_hpd_irq_event(adv7511->connector.dev);
schedule_work(&adv7511->hpd_work);
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true;
@ -546,23 +574,20 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
/* Reading the EDID only works if the device is powered */
if (!adv7511->powered) {
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, 0);
if (adv7511->i2c_main->irq) {
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
ADV7511_INT1_DDC_ERROR);
}
adv7511->current_edid_segment = -1;
unsigned int edid_i2c_addr =
(adv7511->i2c_main->addr << 1) + 4;
__adv7511_power_on(adv7511);
/* Reset the EDID_I2C_ADDR register as it might be cleared */
regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
edid_i2c_addr);
}
edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
if (!adv7511->powered)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN);
__adv7511_power_off(adv7511);
kfree(adv7511->edid);
adv7511->edid = edid;
@ -825,6 +850,10 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
if (adv->type == ADV7533)
ret = adv7533_attach_dsi(adv);
if (adv->i2c_main->irq)
regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0),
ADV7511_INT0_HPD);
return ret;
}
@ -839,6 +868,58 @@ static struct drm_bridge_funcs adv7511_bridge_funcs = {
* Probe & remove
*/
static const char * const adv7511_supply_names[] = {
"avdd",
"dvdd",
"pvdd",
"bgvdd",
"dvdd-3v",
};
static const char * const adv7533_supply_names[] = {
"avdd",
"dvdd",
"pvdd",
"a2vdd",
"v3p3",
"v1p2",
};
static int adv7511_init_regulators(struct adv7511 *adv)
{
struct device *dev = &adv->i2c_main->dev;
const char * const *supply_names;
unsigned int i;
int ret;
if (adv->type == ADV7511) {
supply_names = adv7511_supply_names;
adv->num_supplies = ARRAY_SIZE(adv7511_supply_names);
} else {
supply_names = adv7533_supply_names;
adv->num_supplies = ARRAY_SIZE(adv7533_supply_names);
}
adv->supplies = devm_kcalloc(dev, adv->num_supplies,
sizeof(*adv->supplies), GFP_KERNEL);
if (!adv->supplies)
return -ENOMEM;
for (i = 0; i < adv->num_supplies; i++)
adv->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(dev, adv->num_supplies, adv->supplies);
if (ret)
return ret;
return regulator_bulk_enable(adv->num_supplies, adv->supplies);
}
static void adv7511_uninit_regulators(struct adv7511 *adv)
{
regulator_bulk_disable(adv->num_supplies, adv->supplies);
}
static int adv7511_parse_dt(struct device_node *np,
struct adv7511_link_config *config)
{
@ -939,6 +1020,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
if (!adv7511)
return -ENOMEM;
adv7511->i2c_main = i2c;
adv7511->powered = false;
adv7511->status = connector_status_disconnected;
@ -956,13 +1038,21 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
if (ret)
return ret;
ret = adv7511_init_regulators(adv7511);
if (ret) {
dev_err(dev, "failed to init regulators\n");
return ret;
}
/*
* The power down GPIO is optional. If present, toggle it from active to
* inactive to wake up the encoder.
*/
adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH);
if (IS_ERR(adv7511->gpio_pd))
return PTR_ERR(adv7511->gpio_pd);
if (IS_ERR(adv7511->gpio_pd)) {
ret = PTR_ERR(adv7511->gpio_pd);
goto uninit_regulators;
}
if (adv7511->gpio_pd) {
mdelay(5);
@ -970,12 +1060,14 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
}
adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config);
if (IS_ERR(adv7511->regmap))
return PTR_ERR(adv7511->regmap);
if (IS_ERR(adv7511->regmap)) {
ret = PTR_ERR(adv7511->regmap);
goto uninit_regulators;
}
ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val);
if (ret)
return ret;
goto uninit_regulators;
dev_dbg(dev, "Rev. %d\n", val);
if (adv7511->type == ADV7511)
@ -985,7 +1077,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
else
ret = adv7533_patch_registers(adv7511);
if (ret)
return ret;
goto uninit_regulators;
regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
@ -995,10 +1087,11 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
adv7511_packet_disable(adv7511, 0xffff);
adv7511->i2c_main = i2c;
adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1);
if (!adv7511->i2c_edid)
return -ENOMEM;
if (!adv7511->i2c_edid) {
ret = -ENOMEM;
goto uninit_regulators;
}
if (adv7511->type == ADV7533) {
ret = adv7533_init_cec(adv7511);
@ -1006,6 +1099,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_i2c_unregister_edid;
}
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
if (i2c->irq) {
init_waitqueue_head(&adv7511->wq);
@ -1045,6 +1140,8 @@ err_unregister_cec:
adv7533_uninit_cec(adv7511);
err_i2c_unregister_edid:
i2c_unregister_device(adv7511->i2c_edid);
uninit_regulators:
adv7511_uninit_regulators(adv7511);
return ret;
}
@ -1058,6 +1155,8 @@ static int adv7511_remove(struct i2c_client *i2c)
adv7533_uninit_cec(adv7511);
}
adv7511_uninit_regulators(adv7511);
drm_bridge_remove(&adv7511->bridge);
adv7511_audio_exit(adv7511);

View File

@ -113,13 +113,20 @@ struct dw_hdmi_i2c {
bool is_regaddr;
};
struct dw_hdmi_phy_data {
enum dw_hdmi_phy_type type;
const char *name;
bool has_svsret;
};
struct dw_hdmi {
struct drm_connector connector;
struct drm_encoder *encoder;
struct drm_bridge *bridge;
struct drm_bridge bridge;
enum dw_hdmi_devtype dev_type;
unsigned int version;
struct platform_device *audio;
enum dw_hdmi_devtype dev_type;
struct device *dev;
struct clk *isfr_clk;
struct clk *iahb_clk;
@ -133,7 +140,9 @@ struct dw_hdmi {
u8 edid[HDMI_EDID_LEN];
bool cable_plugin;
const struct dw_hdmi_phy_data *phy;
bool phy_enabled;
struct drm_display_mode previous_mode;
struct i2c_adapter *ddc;
@ -868,7 +877,7 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
return true;
}
static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
unsigned char addr)
{
hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
@ -882,13 +891,6 @@ static void __hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
hdmi_phy_wait_i2c_done(hdmi, 1000);
}
static int hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
unsigned char addr)
{
__hdmi_phy_i2c_write(hdmi, data, addr);
return 0;
}
static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
{
hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0,
@ -903,11 +905,11 @@ static void dw_hdmi_phy_enable_tmds(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_ENTMDS_MASK);
}
static void dw_hdmi_phy_enable_spare(struct dw_hdmi *hdmi, u8 enable)
static void dw_hdmi_phy_enable_svsret(struct dw_hdmi *hdmi, u8 enable)
{
hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
HDMI_PHY_CONF0_SPARECTRL_OFFSET,
HDMI_PHY_CONF0_SPARECTRL_MASK);
HDMI_PHY_CONF0_SVSRET_OFFSET,
HDMI_PHY_CONF0_SVSRET_MASK);
}
static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable)
@ -938,34 +940,14 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
HDMI_PHY_CONF0_SELDIPIF_MASK);
}
static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
unsigned char res, int cscon)
static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon)
{
unsigned res_idx;
u8 val, msec;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
if (prep)
return -EINVAL;
switch (res) {
case 0: /* color resolution 0 is 8 bit colour depth */
case 8:
res_idx = DW_HDMI_RES_8;
break;
case 10:
res_idx = DW_HDMI_RES_10;
break;
case 12:
res_idx = DW_HDMI_RES_12;
break;
default:
return -EINVAL;
}
/* PLL/MPLL Cfg - always match on final entry */
for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
if (hdmi->hdmi_data.video_mode.mpixelclock <=
@ -1004,9 +986,13 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
/* gen2 pddq */
dw_hdmi_phy_gen2_pddq(hdmi, 1);
/* PHY reset */
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
/* Leave low power consumption mode by asserting SVSRET. */
if (hdmi->phy->has_svsret)
dw_hdmi_phy_enable_svsret(hdmi, 1);
/* PHY reset. The reset signal is active high on Gen2 PHYs. */
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_PHYRSTZ, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, 0, HDMI_MC_PHYRSTZ);
hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
@ -1015,21 +1001,26 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
HDMI_PHY_I2CM_SLAVE_ADDR);
hdmi_phy_test_clear(hdmi, 0);
hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].cpce, 0x06);
hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15);
hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
HDMI_3D_TX_PHY_CPCE_CTRL);
hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
HDMI_3D_TX_PHY_GMPCTRL);
hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
HDMI_3D_TX_PHY_CURRCTRL);
/* CURRCTRL */
hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10);
hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
HDMI_3D_TX_PHY_MSM_CTRL);
hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
HDMI_3D_TX_PHY_CKSYMTXCTRL);
hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
HDMI_3D_TX_PHY_VLEVCTRL);
hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */
hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
/* REMOVE CLK TERM */
hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
/* Override and disable clock termination. */
hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
HDMI_3D_TX_PHY_CKCALCTRL);
dw_hdmi_phy_enable_powerdown(hdmi, false);
@ -1041,10 +1032,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep,
dw_hdmi_phy_gen2_txpwron(hdmi, 1);
dw_hdmi_phy_gen2_pddq(hdmi, 0);
if (hdmi->dev_type == RK3288_HDMI)
dw_hdmi_phy_enable_spare(hdmi, 1);
/*Wait for PHY PLL lock */
/* Wait for PHY PLL lock */
msec = 5;
do {
val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
@ -1079,7 +1067,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
dw_hdmi_phy_enable_powerdown(hdmi, true);
/* Enable CSC */
ret = hdmi_phy_configure(hdmi, 0, 8, cscon);
ret = hdmi_phy_configure(hdmi, cscon);
if (ret)
return ret;
}
@ -1351,19 +1339,38 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
/* Workaround to clear the overflow condition */
static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
{
int count;
unsigned int count;
unsigned int i;
u8 val;
/*
* Under some circumstances the Frame Composer arithmetic unit can miss
* an FC register write due to being busy processing the previous one.
* The issue can be worked around by issuing a TMDS software reset and
* then write one of the FC registers several times.
*
* The number of iterations matters and depends on the HDMI TX revision
* (and possibly on the platform). So far only i.MX6Q (v1.30a) and
* i.MX6DL (v1.31a) have been identified as needing the workaround, with
* 4 and 1 iterations respectively.
*/
switch (hdmi->version) {
case 0x130a:
count = 4;
break;
case 0x131a:
count = 1;
break;
default:
return;
}
/* TMDS software reset */
hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
if (hdmi->dev_type == IMX6DL_HDMI) {
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
return;
}
for (count = 0; count < 4; count++)
for (i = 0; i < count; i++)
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
}
@ -1586,42 +1593,6 @@ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
}
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *orig_mode,
struct drm_display_mode *mode)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
/* Store the display mode for plugin/DKMS poweron events */
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
hdmi->disabled = true;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
hdmi->disabled = false;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
static enum drm_connector_status
dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
@ -1714,7 +1685,63 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
.best_encoder = drm_atomic_helper_best_encoder,
};
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
struct drm_encoder *encoder = bridge->encoder;
struct drm_connector *connector = &hdmi->connector;
connector->interlace_allowed = 1;
connector->polled = DRM_CONNECTOR_POLL_HPD;
drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
drm_mode_connector_attach_encoder(connector, encoder);
return 0;
}
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *orig_mode,
struct drm_display_mode *mode)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
/* Store the display mode for plugin/DKMS poweron events */
memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
hdmi->disabled = true;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
mutex_lock(&hdmi->mutex);
hdmi->disabled = false;
dw_hdmi_update_power(hdmi);
dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex);
}
static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.attach = dw_hdmi_bridge_attach,
.enable = dw_hdmi_bridge_enable,
.disable = dw_hdmi_bridge_disable,
.mode_set = dw_hdmi_bridge_mode_set,
@ -1816,7 +1843,8 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
dev_dbg(hdmi->dev, "EVENT=%s\n",
phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
drm_helper_hpd_irq_event(hdmi->bridge->dev);
if (hdmi->bridge.dev)
drm_helper_hpd_irq_event(hdmi->bridge.dev);
}
hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
@ -1826,67 +1854,80 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
{
.type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
.name = "DWC HDMI TX PHY",
}, {
.type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
.name = "DWC MHL PHY + HEAC PHY",
.has_svsret = true,
}, {
.type = DW_HDMI_PHY_DWC_MHL_PHY,
.name = "DWC MHL PHY",
.has_svsret = true,
}, {
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
.name = "DWC HDMI 3D TX PHY + HEAC PHY",
}, {
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
.name = "DWC HDMI 3D TX PHY",
}, {
.type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
.name = "DWC HDMI 2.0 TX PHY",
.has_svsret = true,
}
};
static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
{
struct drm_encoder *encoder = hdmi->encoder;
struct drm_bridge *bridge;
int ret;
unsigned int i;
u8 phy_type;
bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n");
return -ENOMEM;
phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
if (dw_hdmi_phys[i].type == phy_type) {
hdmi->phy = &dw_hdmi_phys[i];
return 0;
}
}
hdmi->bridge = bridge;
bridge->driver_private = hdmi;
bridge->funcs = &dw_hdmi_bridge_funcs;
ret = drm_bridge_attach(encoder, bridge, NULL);
if (ret) {
DRM_ERROR("Failed to initialize bridge with drm\n");
return -EINVAL;
}
if (phy_type == DW_HDMI_PHY_VENDOR_PHY)
dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n");
else
dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n",
phy_type);
hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
drm_connector_helper_add(&hdmi->connector,
&dw_hdmi_connector_helper_funcs);
drm_connector_init(drm, &hdmi->connector,
&dw_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
return 0;
return -ENODEV;
}
int dw_hdmi_bind(struct device *dev, struct device *master,
void *data, struct drm_encoder *encoder,
struct resource *iores, int irq,
const struct dw_hdmi_plat_data *plat_data)
static struct dw_hdmi *
__dw_hdmi_probe(struct platform_device *pdev,
const struct dw_hdmi_plat_data *plat_data)
{
struct drm_device *drm = data;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct platform_device_info pdevinfo;
struct device_node *ddc_node;
struct dw_hdmi *hdmi;
struct resource *iores;
int irq;
int ret;
u32 val = 1;
u8 prod_id0;
u8 prod_id1;
u8 config0;
u8 config1;
u8 config3;
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
hdmi->connector.interlace_allowed = 1;
return ERR_PTR(-ENOMEM);
hdmi->plat_data = plat_data;
hdmi->dev = dev;
hdmi->dev_type = plat_data->dev_type;
hdmi->sample_rate = 48000;
hdmi->encoder = encoder;
hdmi->disabled = true;
hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
@ -1908,7 +1949,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
break;
default:
dev_err(dev, "reg-io-width must be 1 or 4\n");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
@ -1917,13 +1958,14 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
of_node_put(ddc_node);
if (!hdmi->ddc) {
dev_dbg(hdmi->dev, "failed to read ddc node\n");
return -EPROBE_DEFER;
return ERR_PTR(-EPROBE_DEFER);
}
} else {
dev_dbg(hdmi->dev, "no ddc property found\n");
}
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hdmi->regs = devm_ioremap_resource(dev, iores);
if (IS_ERR(hdmi->regs)) {
ret = PTR_ERR(hdmi->regs);
@ -1957,15 +1999,36 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
}
/* Product and revision IDs */
dev_info(dev,
"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
hdmi_readb(hdmi, HDMI_DESIGN_ID),
hdmi_readb(hdmi, HDMI_REVISION_ID),
hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
| (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0);
prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1);
if (prod_id0 != HDMI_PRODUCT_ID0_HDMI_TX ||
(prod_id1 & ~HDMI_PRODUCT_ID1_HDCP) != HDMI_PRODUCT_ID1_HDMI_TX) {
dev_err(dev, "Unsupported HDMI controller (%04x:%02x:%02x)\n",
hdmi->version, prod_id0, prod_id1);
ret = -ENODEV;
goto err_iahb;
}
ret = dw_hdmi_detect_phy(hdmi);
if (ret < 0)
goto err_iahb;
dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n",
hdmi->version >> 12, hdmi->version & 0xfff,
prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
hdmi->phy->name);
initialize_hdmi_ih_mutes(hdmi);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err_iahb;
}
ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
dw_hdmi_irq, IRQF_SHARED,
dev_name(dev), hdmi);
@ -1995,11 +2058,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
HDMI_IH_PHY_STAT0);
ret = dw_hdmi_fb_registered(hdmi);
if (ret)
goto err_iahb;
hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
hdmi->bridge.of_node = pdev->dev.of_node;
ret = dw_hdmi_register(drm, hdmi);
ret = dw_hdmi_fb_registered(hdmi);
if (ret)
goto err_iahb;
@ -2012,9 +2075,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
pdevinfo.id = PLATFORM_DEVID_AUTO;
config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID);
config1 = hdmi_readb(hdmi, HDMI_CONFIG1_ID);
config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
if (config1 & HDMI_CONFIG1_AHB) {
if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
struct dw_hdmi_audio_data audio;
audio.phys = iores->start;
@ -2046,9 +2109,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
if (hdmi->i2c)
dw_hdmi_i2c_init(hdmi);
dev_set_drvdata(dev, hdmi);
platform_set_drvdata(pdev, hdmi);
return 0;
return hdmi;
err_iahb:
if (hdmi->i2c) {
@ -2062,14 +2125,11 @@ err_isfr:
err_res:
i2c_put_adapter(hdmi->ddc);
return ret;
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dw_hdmi_bind);
void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
{
struct dw_hdmi *hdmi = dev_get_drvdata(dev);
if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio);
@ -2084,6 +2144,70 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
else
i2c_put_adapter(hdmi->ddc);
}
/* -----------------------------------------------------------------------------
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
int dw_hdmi_probe(struct platform_device *pdev,
const struct dw_hdmi_plat_data *plat_data)
{
struct dw_hdmi *hdmi;
int ret;
hdmi = __dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
return PTR_ERR(hdmi);
ret = drm_bridge_add(&hdmi->bridge);
if (ret < 0) {
__dw_hdmi_remove(hdmi);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(dw_hdmi_probe);
void dw_hdmi_remove(struct platform_device *pdev)
{
struct dw_hdmi *hdmi = platform_get_drvdata(pdev);
drm_bridge_remove(&hdmi->bridge);
__dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_remove);
/* -----------------------------------------------------------------------------
* Bind/unbind API, used from platforms based on the component framework.
*/
int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
const struct dw_hdmi_plat_data *plat_data)
{
struct dw_hdmi *hdmi;
int ret;
hdmi = __dw_hdmi_probe(pdev, plat_data);
if (IS_ERR(hdmi))
return PTR_ERR(hdmi);
ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
if (ret) {
dw_hdmi_remove(pdev);
DRM_ERROR("Failed to initialize bridge with drm\n");
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(dw_hdmi_bind);
void dw_hdmi_unbind(struct device *dev)
{
struct dw_hdmi *hdmi = dev_get_drvdata(dev);
__dw_hdmi_remove(hdmi);
}
EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");

View File

@ -545,12 +545,24 @@
#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12
enum {
/* PRODUCT_ID0 field values */
HDMI_PRODUCT_ID0_HDMI_TX = 0xa0,
/* PRODUCT_ID1 field values */
HDMI_PRODUCT_ID1_HDCP = 0xc0,
HDMI_PRODUCT_ID1_HDMI_RX = 0x02,
HDMI_PRODUCT_ID1_HDMI_TX = 0x01,
/* CONFIG0_ID field values */
HDMI_CONFIG0_I2S = 0x10,
/* CONFIG1_ID field values */
HDMI_CONFIG1_AHB = 0x01,
/* CONFIG3_ID field values */
HDMI_CONFIG3_AHBAUDDMA = 0x02,
HDMI_CONFIG3_GPAUD = 0x01,
/* IH_FC_INT2 field values */
HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
@ -847,8 +859,8 @@ enum {
HDMI_PHY_CONF0_PDZ_OFFSET = 7,
HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20,
HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5,
HDMI_PHY_CONF0_SVSRET_MASK = 0x20,
HDMI_PHY_CONF0_SVSRET_OFFSET = 5,
HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
@ -977,8 +989,7 @@ enum {
HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
/* MC_PHYRSTZ field values */
HDMI_MC_PHYRSTZ_ASSERT = 0x0,
HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01,
/* MC_HEACPHY_RST field values */
HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
@ -1073,4 +1084,70 @@ enum {
HDMI_I2CM_CTLINT_ARB_MASK = 0x4,
};
/*
* HDMI 3D TX PHY registers
*/
#define HDMI_3D_TX_PHY_PWRCTRL 0x00
#define HDMI_3D_TX_PHY_SERDIVCTRL 0x01
#define HDMI_3D_TX_PHY_SERCKCTRL 0x02
#define HDMI_3D_TX_PHY_SERCKKILLCTRL 0x03
#define HDMI_3D_TX_PHY_TXRESCTRL 0x04
#define HDMI_3D_TX_PHY_CKCALCTRL 0x05
#define HDMI_3D_TX_PHY_CPCE_CTRL 0x06
#define HDMI_3D_TX_PHY_TXCLKMEASCTRL 0x07
#define HDMI_3D_TX_PHY_TXMEASCTRL 0x08
#define HDMI_3D_TX_PHY_CKSYMTXCTRL 0x09
#define HDMI_3D_TX_PHY_CMPSEQCTRL 0x0a
#define HDMI_3D_TX_PHY_CMPPWRCTRL 0x0b
#define HDMI_3D_TX_PHY_CMPMODECTRL 0x0c
#define HDMI_3D_TX_PHY_MEASCTRL 0x0d
#define HDMI_3D_TX_PHY_VLEVCTRL 0x0e
#define HDMI_3D_TX_PHY_D2ACTRL 0x0f
#define HDMI_3D_TX_PHY_CURRCTRL 0x10
#define HDMI_3D_TX_PHY_DRVANACTRL 0x11
#define HDMI_3D_TX_PHY_PLLMEASCTRL 0x12
#define HDMI_3D_TX_PHY_PLLPHBYCTRL 0x13
#define HDMI_3D_TX_PHY_GRP_CTRL 0x14
#define HDMI_3D_TX_PHY_GMPCTRL 0x15
#define HDMI_3D_TX_PHY_MPLLMEASCTRL 0x16
#define HDMI_3D_TX_PHY_MSM_CTRL 0x17
#define HDMI_3D_TX_PHY_SCRPB_STATUS 0x18
#define HDMI_3D_TX_PHY_TXTERM 0x19
#define HDMI_3D_TX_PHY_PTRPT_ENBL 0x1a
#define HDMI_3D_TX_PHY_PATTERNGEN 0x1b
#define HDMI_3D_TX_PHY_SDCAP_MODE 0x1c
#define HDMI_3D_TX_PHY_SCOPEMODE 0x1d
#define HDMI_3D_TX_PHY_DIGTXMODE 0x1e
#define HDMI_3D_TX_PHY_STR_STATUS 0x1f
#define HDMI_3D_TX_PHY_SCOPECNT0 0x20
#define HDMI_3D_TX_PHY_SCOPECNT1 0x21
#define HDMI_3D_TX_PHY_SCOPECNT2 0x22
#define HDMI_3D_TX_PHY_SCOPECNTCLK 0x23
#define HDMI_3D_TX_PHY_SCOPESAMPLE 0x24
#define HDMI_3D_TX_PHY_SCOPECNTMSB01 0x25
#define HDMI_3D_TX_PHY_SCOPECNTMSB2CK 0x26
/* HDMI_3D_TX_PHY_CKCALCTRL values */
#define HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE BIT(15)
/* HDMI_3D_TX_PHY_MSM_CTRL values */
#define HDMI_3D_TX_PHY_MSM_CTRL_MPLL_PH_SEL_CK BIT(13)
#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_CLK_REF_MPLL (0 << 1)
#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_OFF (1 << 1)
#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_PCLK (2 << 1)
#define HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK (3 << 1)
#define HDMI_3D_TX_PHY_MSM_CTRL_SCOPE_CK_SEL BIT(0)
/* HDMI_3D_TX_PHY_PTRPT_ENBL values */
#define HDMI_3D_TX_PHY_PTRPT_ENBL_OVERRIDE BIT(15)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT2 BIT(8)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT1 BIT(7)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_PG_SKIP_BIT0 BIT(6)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_CK_REF_ENB BIT(5)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_RCAL_ENB BIT(4)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_CLK_ALIGN_ENB BIT(3)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_TX_READY BIT(2)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_CKO_WORD_ENB BIT(1)
#define HDMI_3D_TX_PHY_PTRPT_ENBL_REFCLK_ENB BIT(0)
#endif /* __DW_HDMI_H__ */

View File

@ -2708,6 +2708,44 @@ backoff:
}
EXPORT_SYMBOL(drm_atomic_helper_connector_set_property);
static int page_flip_common(
struct drm_atomic_state *state,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
{
struct drm_plane *plane = crtc->primary;
struct drm_plane_state *plane_state;
struct drm_crtc_state *crtc_state;
int ret = 0;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
crtc_state->event = event;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state))
return PTR_ERR(plane_state);
ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
if (ret != 0)
return ret;
drm_atomic_set_fb_for_plane(plane_state, fb);
/* Make sure we don't accidentally do a full modeset. */
state->allow_modeset = false;
if (!crtc_state->active) {
DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n",
crtc->base.id);
return -EINVAL;
}
return ret;
}
/**
* drm_atomic_helper_page_flip - execute a legacy page flip
* @crtc: DRM crtc
@ -2715,7 +2753,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property);
* @event: optional DRM event to signal upon completion
* @flags: flip flags for non-vblank sync'ed updates
*
* Provides a default page flip implementation using the atomic driver interface.
* Provides a default &drm_crtc_funcs.page_flip implementation
* using the atomic driver interface.
*
* Note that for now so called async page flips (i.e. updates which are not
* synchronized to vblank) are not supported, since the atomic interfaces have
@ -2723,6 +2762,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property);
*
* Returns:
* Returns 0 on success, negative errno numbers on failure.
*
* See also:
* drm_atomic_helper_page_flip_target()
*/
int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
@ -2731,8 +2773,6 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
{
struct drm_plane *plane = crtc->primary;
struct drm_atomic_state *state;
struct drm_plane_state *plane_state;
struct drm_crtc_state *crtc_state;
int ret = 0;
if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
@ -2743,35 +2783,14 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
return -ENOMEM;
state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
retry:
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state)) {
ret = PTR_ERR(crtc_state);
goto fail;
}
crtc_state->event = event;
plane_state = drm_atomic_get_plane_state(state, plane);
if (IS_ERR(plane_state)) {
ret = PTR_ERR(plane_state);
goto fail;
}
ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
ret = page_flip_common(state, crtc, fb, event);
if (ret != 0)
goto fail;
drm_atomic_set_fb_for_plane(plane_state, fb);
/* Make sure we don't accidentally do a full modeset. */
state->allow_modeset = false;
if (!crtc_state->active) {
DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n",
crtc->base.id);
ret = -EINVAL;
goto fail;
}
ret = drm_atomic_nonblocking_commit(state);
fail:
if (ret == -EDEADLK)
goto backoff;
@ -2794,6 +2813,78 @@ backoff:
}
EXPORT_SYMBOL(drm_atomic_helper_page_flip);
/**
* drm_atomic_helper_page_flip_target - do page flip on target vblank period.
* @crtc: DRM crtc
* @fb: DRM framebuffer
* @event: optional DRM event to signal upon completion
* @flags: flip flags for non-vblank sync'ed updates
* @target: specifying the target vblank period when the flip to take effect
*
* Provides a default &drm_crtc_funcs.page_flip_target implementation.
* Similar to drm_atomic_helper_page_flip() with extra parameter to specify
* target vblank period to flip.
*
* Returns:
* Returns 0 on success, negative errno numbers on failure.
*/
int drm_atomic_helper_page_flip_target(
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags,
uint32_t target)
{
struct drm_plane *plane = crtc->primary;
struct drm_atomic_state *state;
struct drm_crtc_state *crtc_state;
int ret = 0;
if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
return -EINVAL;
state = drm_atomic_state_alloc(plane->dev);
if (!state)
return -ENOMEM;
state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
retry:
ret = page_flip_common(state, crtc, fb, event);
if (ret != 0)
goto fail;
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
if (WARN_ON(!crtc_state)) {
ret = -EINVAL;
goto fail;
}
crtc_state->target_vblank = target;
ret = drm_atomic_nonblocking_commit(state);
fail:
if (ret == -EDEADLK)
goto backoff;
drm_atomic_state_put(state);
return ret;
backoff:
drm_atomic_state_clear(state);
drm_atomic_legacy_backoff(state);
/*
* Someone might have exchanged the framebuffer while we dropped locks
* in the backoff code. We need to fix up the fb refcount tracking the
* core does for us.
*/
plane->old_fb = plane->fb;
goto retry;
}
EXPORT_SYMBOL(drm_atomic_helper_page_flip_target);
/**
* drm_atomic_helper_connector_dpms() - connector dpms helper implementation
* @connector: affected connector

View File

@ -67,6 +67,14 @@ static void drm_cache_flush_clflush(struct page *pages[],
}
#endif
/**
* drm_clflush_pages - Flush dcache lines of a set of pages.
* @pages: List of pages to be flushed.
* @num_pages: Number of pages in the array.
*
* Flush every data cache line entry that points to an address belonging
* to a page in the array.
*/
void
drm_clflush_pages(struct page *pages[], unsigned long num_pages)
{
@ -101,6 +109,13 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
}
EXPORT_SYMBOL(drm_clflush_pages);
/**
* drm_clflush_sg - Flush dcache lines pointing to a scather-gather.
* @st: struct sg_table.
*
* Flush every data cache line entry that points to an address in the
* sg.
*/
void
drm_clflush_sg(struct sg_table *st)
{
@ -125,6 +140,14 @@ drm_clflush_sg(struct sg_table *st)
}
EXPORT_SYMBOL(drm_clflush_sg);
/**
* drm_clflush_virt_range - Flush dcache lines of a region
* @addr: Initial kernel memory address.
* @length: Region size.
*
* Flush every data cache line entry that points to an address in the
* region requested.
*/
void
drm_clflush_virt_range(void *addr, unsigned long length)
{

View File

@ -473,8 +473,7 @@ drm_fbdev_cma_create(struct drm_fb_helper *helper,
return 0;
err_cma_destroy:
drm_framebuffer_unregister_private(&fbdev_cma->fb->fb);
drm_fb_cma_destroy(&fbdev_cma->fb->fb);
drm_framebuffer_remove(&fbdev_cma->fb->fb);
err_fb_info_destroy:
drm_fb_helper_release_fbi(helper);
err_gem_free_object:
@ -574,10 +573,8 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
drm_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev);
drm_fb_helper_release_fbi(&fbdev_cma->fb_helper);
if (fbdev_cma->fb) {
drm_framebuffer_unregister_private(&fbdev_cma->fb->fb);
drm_fb_cma_destroy(&fbdev_cma->fb->fb);
}
if (fbdev_cma->fb)
drm_framebuffer_remove(&fbdev_cma->fb->fb);
drm_fb_helper_fini(&fbdev_cma->fb_helper);
kfree(fbdev_cma);

View File

@ -3,6 +3,7 @@ config DRM_ETNAVIV
tristate "ETNAVIV (DRM support for Vivante GPU IP cores)"
depends on DRM
depends on ARCH_MXC || ARCH_DOVE
depends on MMU
select SHMEM
select TMPFS
select IOMMU_API

View File

@ -467,7 +467,7 @@ err:
clk_disable_unprepare(ctx->clks[i]);
}
static struct exynos_drm_crtc_ops decon_crtc_ops = {
static const struct exynos_drm_crtc_ops decon_crtc_ops = {
.enable = decon_enable,
.disable = decon_disable,
.enable_vblank = decon_enable_vblank,

View File

@ -109,9 +109,6 @@ static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_private *private = crtc->dev->dev_private;
private->crtc[exynos_crtc->pipe] = NULL;
drm_crtc_cleanup(crtc);
kfree(exynos_crtc);
@ -134,7 +131,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
void *ctx)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = drm_dev->dev_private;
struct drm_crtc *crtc;
int ret;
@ -149,8 +145,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc;
ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
&exynos_crtc_funcs, NULL);
if (ret < 0)

View File

@ -211,12 +211,6 @@ struct drm_exynos_file_private {
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
/*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
struct device *dma_dev;
void *mapping;
@ -231,9 +225,9 @@ struct exynos_drm_private {
static inline struct exynos_drm_crtc *
exynos_drm_crtc_from_pipe(struct drm_device *dev, int pipe)
{
struct exynos_drm_private *private = dev->dev_private;
struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
return to_exynos_crtc(private->crtc[pipe]);
return to_exynos_crtc(crtc);
}
static inline struct device *to_dma_dev(struct drm_device *dev)

View File

@ -304,8 +304,8 @@ static void ade_set_medianoc_qos(struct ade_crtc *acrtc)
static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe)
{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
@ -320,8 +320,8 @@ static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe)
static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe)
{
struct kirin_drm_private *priv = dev->dev_private;
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]);
struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base;
@ -575,7 +575,6 @@ static const struct drm_crtc_funcs ade_crtc_funcs = {
static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *plane)
{
struct kirin_drm_private *priv = dev->dev_private;
struct device_node *port;
int ret;
@ -599,7 +598,6 @@ static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
}
drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
priv->crtc[drm_crtc_index(crtc)] = crtc;
return 0;
}

View File

@ -20,7 +20,6 @@ struct kirin_dc_ops {
};
struct kirin_drm_private {
struct drm_crtc *crtc[MAX_CRTC];
#ifdef CONFIG_DRM_FBDEV_EMULATION
struct drm_fbdev_cma *fbdev;
#endif

View File

@ -49,6 +49,7 @@
#include <drm/drm_legacy.h> /* for struct drm_dma_handle */
#include <drm/drm_gem.h>
#include <drm/drm_auth.h>
#include <drm/drm_cache.h>
#include "i915_params.h"
#include "i915_reg.h"

View File

@ -207,8 +207,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct drm_encoder *encoder;
struct imx_hdmi *hdmi;
struct resource *iores;
int irq;
int ret;
if (!pdev->dev.of_node)
@ -223,14 +221,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
hdmi->dev = &pdev->dev;
encoder = &hdmi->encoder;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iores)
return -ENXIO;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
/*
* If we failed to find the CRTC(s) which this encoder is
@ -249,7 +239,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
ret = dw_hdmi_bind(pdev, encoder, plat_data);
/*
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
@ -264,7 +254,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
static void dw_hdmi_imx_unbind(struct device *dev, struct device *master,
void *data)
{
return dw_hdmi_unbind(dev, master, data);
return dw_hdmi_unbind(dev);
}
static const struct component_ops dw_hdmi_imx_ops = {

View File

@ -170,8 +170,8 @@ static void mtk_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
{
struct mtk_drm_private *priv = drm->dev_private;
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base);
@ -181,8 +181,8 @@ int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
{
struct mtk_drm_private *priv = drm->dev_private;
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]);
struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
mtk_ddp_comp_disable_vblank(ovl);
@ -588,7 +588,6 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
goto unprepare;
drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE);
priv->crtc[pipe] = &mtk_crtc->base;
priv->num_pipes++;
return 0;

View File

@ -32,7 +32,6 @@ struct mtk_drm_private {
struct drm_device *drm;
struct device *dma_dev;
struct drm_crtc *crtc[MAX_CRTC];
unsigned int num_pipes;
struct device_node *mutex_node;

View File

@ -4,6 +4,7 @@ config DRM_MSM
depends on DRM
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
depends on OF && COMMON_CLK
depends on MMU
select REGULATOR
select DRM_KMS_HELPER
select DRM_PANEL

View File

@ -174,10 +174,8 @@ fail_unlock:
fail:
if (ret) {
if (fb) {
drm_framebuffer_unregister_private(fb);
if (fb)
drm_framebuffer_remove(fb);
}
}
return ret;
@ -247,7 +245,6 @@ void msm_fbdev_free(struct drm_device *dev)
/* this will free the backing object */
if (fbdev->fb) {
msm_gem_put_vaddr(fbdev->bo);
drm_framebuffer_unregister_private(fbdev->fb);
drm_framebuffer_remove(fbdev->fb);
}

View File

@ -58,27 +58,30 @@ int
nouveau_display_vblank_enable(struct drm_device *dev, unsigned int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
if (nv_crtc->index == pipe) {
nvif_notify_get(&nv_crtc->vblank);
return 0;
}
}
return -EINVAL;
struct nouveau_crtc *nv_crtc;
crtc = drm_crtc_from_index(dev, pipe);
if (!crtc)
return -EINVAL;
nv_crtc = nouveau_crtc(crtc);
nvif_notify_get(&nv_crtc->vblank);
return 0;
}
void
nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
if (nv_crtc->index == pipe) {
nvif_notify_put(&nv_crtc->vblank);
return;
}
}
struct nouveau_crtc *nv_crtc;
crtc = drm_crtc_from_index(dev, pipe);
if (!crtc)
return;
nv_crtc = nouveau_crtc(crtc);
nvif_notify_put(&nv_crtc->vblank);
}
static inline int

View File

@ -225,10 +225,8 @@ fail:
drm_fb_helper_release_fbi(helper);
if (fb) {
drm_framebuffer_unregister_private(fb);
if (fb)
drm_framebuffer_remove(fb);
}
}
return ret;
@ -314,10 +312,8 @@ void omap_fbdev_free(struct drm_device *dev)
omap_gem_put_paddr(fbdev->bo);
/* this will free the backing object */
if (fbdev->fb) {
drm_framebuffer_unregister_private(fbdev->fb);
if (fbdev->fb)
drm_framebuffer_remove(fbdev->fb);
}
kfree(fbdev);

View File

@ -84,8 +84,18 @@ int
qxl_debugfs_init(struct drm_minor *minor)
{
#if defined(CONFIG_DEBUG_FS)
int r;
struct qxl_device *dev =
(struct qxl_device *) minor->dev->dev_private;
drm_debugfs_create_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
r = qxl_ttm_debugfs_init(dev);
if (r) {
DRM_ERROR("Failed to init TTM debugfs\n");
return r;
}
#endif
return 0;
}

View File

@ -1077,7 +1077,6 @@ static int qdev_output_init(struct drm_device *dev, int num_output)
dev->mode_config.suggested_x_property, 0);
drm_object_attach_property(&connector->base,
dev->mode_config.suggested_y_property, 0);
drm_connector_register(connector);
return 0;
}

View File

@ -62,20 +62,83 @@ static struct pci_driver qxl_pci_driver;
static int
qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct drm_device *drm;
struct qxl_device *qdev;
int ret;
if (pdev->revision < 4) {
DRM_ERROR("qxl too old, doesn't support client_monitors_config,"
" use xf86-video-qxl in user mode");
return -EINVAL; /* TODO: ENODEV ? */
}
return drm_get_pci_dev(pdev, ent, &qxl_driver);
drm = drm_dev_alloc(&qxl_driver, &pdev->dev);
if (IS_ERR(drm))
return -ENOMEM;
qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL);
if (!qdev) {
ret = -ENOMEM;
goto free_drm_device;
}
ret = pci_enable_device(pdev);
if (ret)
goto free_drm_device;
drm->pdev = pdev;
pci_set_drvdata(pdev, drm);
drm->dev_private = qdev;
ret = qxl_device_init(qdev, drm, pdev, ent->driver_data);
if (ret)
goto disable_pci;
ret = drm_vblank_init(drm, 1);
if (ret)
goto unload;
ret = qxl_modeset_init(qdev);
if (ret)
goto vblank_cleanup;
drm_kms_helper_poll_init(qdev->ddev);
/* Complete initialization. */
ret = drm_dev_register(drm, ent->driver_data);
if (ret)
goto modeset_cleanup;
return 0;
modeset_cleanup:
qxl_modeset_fini(qdev);
vblank_cleanup:
drm_vblank_cleanup(drm);
unload:
qxl_device_fini(qdev);
disable_pci:
pci_disable_device(pdev);
free_drm_device:
kfree(qdev);
kfree(drm);
return ret;
}
static void
qxl_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct qxl_device *qdev = dev->dev_private;
drm_put_dev(dev);
drm_dev_unregister(dev);
qxl_modeset_fini(qdev);
qxl_device_fini(qdev);
dev->dev_private = NULL;
kfree(qdev);
drm_dev_unref(dev);
}
static const struct file_operations qxl_fops = {
@ -230,8 +293,6 @@ static struct pci_driver qxl_pci_driver = {
static struct drm_driver qxl_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
.load = qxl_driver_load,
.unload = qxl_driver_unload,
.get_vblank_counter = qxl_noop_get_vblank_counter,
.enable_vblank = qxl_noop_enable_vblank,
.disable_vblank = qxl_noop_disable_vblank,

View File

@ -336,8 +336,9 @@ __printf(2,3) void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...);
extern const struct drm_ioctl_desc qxl_ioctls[];
extern int qxl_max_ioctl;
int qxl_driver_load(struct drm_device *dev, unsigned long flags);
void qxl_driver_unload(struct drm_device *dev);
int qxl_device_init(struct qxl_device *qdev, struct drm_device *ddev,
struct pci_dev *pdev, unsigned long flags);
void qxl_device_fini(struct qxl_device *qdev);
int qxl_modeset_init(struct qxl_device *qdev);
void qxl_modeset_fini(struct qxl_device *qdev);
@ -531,6 +532,7 @@ int qxl_garbage_collect(struct qxl_device *qdev);
int qxl_debugfs_init(struct drm_minor *minor);
void qxl_debugfs_takedown(struct drm_minor *minor);
int qxl_ttm_debugfs_init(struct qxl_device *qdev);
/* qxl_prime.c */
int qxl_gem_prime_pin(struct drm_gem_object *obj);

View File

@ -115,7 +115,7 @@ static void qxl_gc_work(struct work_struct *work)
qxl_garbage_collect(qdev);
}
static int qxl_device_init(struct qxl_device *qdev,
int qxl_device_init(struct qxl_device *qdev,
struct drm_device *ddev,
struct pci_dev *pdev,
unsigned long flags)
@ -263,7 +263,7 @@ static int qxl_device_init(struct qxl_device *qdev,
return 0;
}
static void qxl_device_fini(struct qxl_device *qdev)
void qxl_device_fini(struct qxl_device *qdev)
{
if (qdev->current_release_bo[0])
qxl_bo_unref(&qdev->current_release_bo[0]);
@ -284,55 +284,3 @@ static void qxl_device_fini(struct qxl_device *qdev)
qdev->mode_info.num_modes = 0;
qxl_debugfs_remove_files(qdev);
}
void qxl_driver_unload(struct drm_device *dev)
{
struct qxl_device *qdev = dev->dev_private;
if (qdev == NULL)
return;
drm_vblank_cleanup(dev);
qxl_modeset_fini(qdev);
qxl_device_fini(qdev);
kfree(qdev);
dev->dev_private = NULL;
}
int qxl_driver_load(struct drm_device *dev, unsigned long flags)
{
struct qxl_device *qdev;
int r;
qdev = kzalloc(sizeof(struct qxl_device), GFP_KERNEL);
if (qdev == NULL)
return -ENOMEM;
dev->dev_private = qdev;
r = qxl_device_init(qdev, dev, dev->pdev, flags);
if (r)
goto out;
r = drm_vblank_init(dev, 1);
if (r)
goto unload;
r = qxl_modeset_init(qdev);
if (r)
goto unload;
drm_kms_helper_poll_init(qdev->ddev);
return 0;
unload:
qxl_driver_unload(dev);
out:
kfree(qdev);
return r;
}

View File

@ -35,7 +35,6 @@
#include "qxl_object.h"
#include <linux/delay.h>
static int qxl_ttm_debugfs_init(struct qxl_device *qdev);
static struct qxl_device *qxl_get_qdev(struct ttm_bo_device *bdev)
{
@ -436,11 +435,6 @@ int qxl_ttm_init(struct qxl_device *qdev)
((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024));
DRM_INFO("qxl: %uM of Surface memory size\n",
(unsigned)qdev->surfaceram_size / (1024 * 1024));
r = qxl_ttm_debugfs_init(qdev);
if (r) {
DRM_ERROR("Failed to init debugfs\n");
return r;
}
return 0;
}
@ -473,7 +467,7 @@ static int qxl_mm_dump_table(struct seq_file *m, void *data)
}
#endif
static int qxl_ttm_debugfs_init(struct qxl_device *qdev)
int qxl_ttm_debugfs_init(struct qxl_device *qdev)
{
#if defined(CONFIG_DEBUG_FS)
static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES];

View File

@ -257,8 +257,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi;
struct resource *iores;
int irq;
int ret;
if (!pdev->dev.of_node)
@ -273,14 +271,6 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
hdmi->dev = &pdev->dev;
encoder = &hdmi->encoder;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iores)
return -ENXIO;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
/*
* If we failed to find the CRTC(s) which this encoder is
@ -301,7 +291,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
drm_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
ret = dw_hdmi_bind(dev, master, data, encoder, iores, irq, plat_data);
ret = dw_hdmi_bind(pdev, encoder, plat_data);
/*
* If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(),
@ -316,7 +306,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
void *data)
{
return dw_hdmi_unbind(dev, master, data);
return dw_hdmi_unbind(dev);
}
static const struct component_ops dw_hdmi_rockchip_ops = {

View File

@ -1274,13 +1274,19 @@ static bool evict_everything(struct drm_mm *mm,
if (drm_mm_scan_add_block(&scan, &e->node))
break;
}
err = 0;
list_for_each_entry(e, &evict_list, link) {
if (!drm_mm_scan_remove_block(&scan, &e->node)) {
pr_err("Node %lld not marked for eviction!\n",
e->node.start);
list_del(&e->link);
if (!err) {
pr_err("Node %lld not marked for eviction!\n",
e->node.start);
err = -EINVAL;
}
}
}
if (err)
return false;
list_for_each_entry(e, &evict_list, link)
drm_mm_remove_node(&e->node);

View File

@ -804,23 +804,10 @@ static const struct file_operations tegra_drm_fops = {
.llseek = noop_llseek,
};
static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm,
unsigned int pipe)
{
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
if (pipe == drm_crtc_index(crtc))
return crtc;
}
return NULL;
}
static u32 tegra_drm_get_vblank_counter(struct drm_device *drm,
unsigned int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc)
@ -831,7 +818,7 @@ static u32 tegra_drm_get_vblank_counter(struct drm_device *drm,
static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc)
@ -844,7 +831,7 @@ static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe)
static void tegra_drm_disable_vblank(struct drm_device *drm, unsigned int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct tegra_dc *dc = to_tegra_dc(crtc);
if (crtc)

View File

@ -271,8 +271,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
return 0;
destroy:
drm_framebuffer_unregister_private(fb);
tegra_fb_destroy(fb);
drm_framebuffer_remove(fb);
release:
drm_fb_helper_release_fbi(helper);
return err;
@ -342,10 +341,8 @@ static void tegra_fbdev_exit(struct tegra_fbdev *fbdev)
drm_fb_helper_unregister_fbi(&fbdev->base);
drm_fb_helper_release_fbi(&fbdev->base);
if (fbdev->fb) {
drm_framebuffer_unregister_private(&fbdev->fb->base);
if (fbdev->fb)
drm_framebuffer_remove(&fbdev->fb->base);
}
drm_fb_helper_fini(&fbdev->base);
tegra_fbdev_free(fbdev);

View File

@ -156,7 +156,8 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
const struct drm_display_mode *mode)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
u32 val;
int fifo_lines;
int vblank_lines;
@ -272,9 +273,7 @@ int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
int *max_error, struct timeval *vblank_time,
unsigned flags)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = &vc4_crtc->base;
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct drm_crtc_state *state = crtc->state;
/* Helper routine in DRM core does all the work: */
@ -652,8 +651,8 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
@ -662,8 +661,8 @@ int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id)
void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
CRTC_WRITE(PV_INTEN, 0);
}
@ -937,7 +936,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_crtc *vc4_crtc;
struct drm_crtc *crtc;
struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp;
@ -975,7 +973,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
&vc4_crtc_funcs, NULL);
drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
primary_plane->crtc = crtc;
vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
vc4_crtc->channel = vc4_crtc->data->hvs_channel;
drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));

View File

@ -16,7 +16,6 @@ struct vc4_dev {
struct vc4_hdmi *hdmi;
struct vc4_hvs *hvs;
struct vc4_crtc *crtc[3];
struct vc4_v3d *v3d;
struct vc4_dpi *dpi;
struct vc4_vec *vec;

View File

@ -31,6 +31,7 @@
#include <drm/drmP.h>
#include <drm/drm_gem.h>
#include <drm/drm_cache.h>
#include <uapi/drm/vgem_drm.h>

View File

@ -27,6 +27,16 @@ enum dw_hdmi_devtype {
RK3288_HDMI,
};
enum dw_hdmi_phy_type {
DW_HDMI_PHY_DWC_HDMI_TX_PHY = 0x00,
DW_HDMI_PHY_DWC_MHL_PHY_HEAC = 0xb2,
DW_HDMI_PHY_DWC_MHL_PHY = 0xc2,
DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC = 0xe2,
DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY = 0xf2,
DW_HDMI_PHY_DWC_HDMI20_TX_PHY = 0xf3,
DW_HDMI_PHY_VENDOR_PHY = 0xfe,
};
struct dw_hdmi_mpll_config {
unsigned long mpixelclock;
struct {
@ -56,10 +66,11 @@ struct dw_hdmi_plat_data {
struct drm_display_mode *mode);
};
void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
int dw_hdmi_bind(struct device *dev, struct device *master,
void *data, struct drm_encoder *encoder,
struct resource *iores, int irq,
int dw_hdmi_probe(struct platform_device *pdev,
const struct dw_hdmi_plat_data *plat_data);
void dw_hdmi_remove(struct platform_device *pdev);
void dw_hdmi_unbind(struct device *dev);
int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
const struct dw_hdmi_plat_data *plat_data);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);

View File

@ -731,11 +731,6 @@ int drm_noop(struct drm_device *dev, void *data,
int drm_invalid_op(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* Cache management (drm_cache.c) */
void drm_clflush_pages(struct page *pages[], unsigned long num_pages);
void drm_clflush_sg(struct sg_table *st);
void drm_clflush_virt_range(void *addr, unsigned long length);
/*
* These are exported to drivers so that they can implement fencing using
* DMA quiscent + idle. DMA quiescent usually requires the hardware lock.

View File

@ -121,6 +121,12 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags);
int drm_atomic_helper_page_flip_target(
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags,
uint32_t target);
int drm_atomic_helper_connector_dpms(struct drm_connector *connector,
int mode);
struct drm_encoder *

View File

@ -33,7 +33,11 @@
#ifndef _DRM_CACHE_H_
#define _DRM_CACHE_H_
#include <linux/scatterlist.h>
void drm_clflush_pages(struct page *pages[], unsigned long num_pages);
void drm_clflush_sg(struct sg_table *st);
void drm_clflush_virt_range(void *addr, unsigned long length);
static inline bool drm_arch_can_wc_memory(void)
{

View File

@ -148,6 +148,15 @@ struct drm_crtc_state {
struct drm_property_blob *ctm;
struct drm_property_blob *gamma_lut;
/**
* @target_vblank:
*
* Target vertical blank period when a page flip
* should take effect.
*/
u32 target_vblank;
/**
* @event:
*

View File

@ -41,10 +41,17 @@ extern "C" {
/* 8 bpp Red */
#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
/* 16 bpp Red */
#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */
/* 16 bpp RG */
#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */
#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
/* 32 bpp RG */
#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */
#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
/* 8 bpp RGB */
#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */
#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */