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", - adi,input-justification: The input bit justification ("left", "evenly",
"right"). "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: The following properties are required for ADV7533:
- adi,dsi-lanes: Number of DSI data lanes connected to the DSI host. It should - adi,dsi-lanes: Number of DSI data lanes connected to the DSI host. It should
be one of 1, 2, 3 or 4. 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: Optional properties:

View File

@ -1,52 +1,33 @@
DesignWare HDMI bridge bindings Synopsys DesignWare HDMI TX Encoder
===================================
Required properties: This document defines device tree properties for the Synopsys DesignWare HDMI
- compatible: platform specific such as: TX Encoder (DWC HDMI TX). It doesn't constitue a device tree binding
* "snps,dw-hdmi-tx" specification by itself but is meant to be referenced by platform-specific
* "fsl,imx6q-hdmi" device tree bindings.
* "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
Optional properties When referenced from platform device tree bindings the properties defined in
- reg-io-width: the width of the reg:1,4, default set to 1 if not present this document are defined as follows. The platform device tree bindings are
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing, responsible for defining whether each property is required or optional.
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"
Example: - reg: Memory mapped base address and length of the DWC HDMI TX registers.
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>;
port@0 { - reg-io-width: Width of the registers specified by the reg property. The
reg = <0>; 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 { - interrupts: Reference to the DWC HDMI TX interrupt.
remote-endpoint = <&ipu1_di0_hdmi>;
};
};
port@1 { - clocks: References to all the clocks specified in the clock-names property
reg = <1>; as specified in Documentation/devicetree/bindings/clock/clock-bindings.txt.
hdmi_mux_1: endpoint { - clock-names: The DWC HDMI TX uses the following clocks.
remote-endpoint = <&ipu1_di1_hdmi>;
}; - "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: 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: - compatible : Shall be one of "fsl,imx6q-hdmi" or "fsl,imx6dl-hdmi".
- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing - 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 { 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: Required properties:
- compatible: "rockchip,rk3288-dw-hdmi";
- reg: Physical base address and length of the controller's registers. - compatible: Shall contain "rockchip,rk3288-dw-hdmi".
- clocks: phandle to hdmi iahb and isfr clocks. - reg: See dw_hdmi.txt.
- clock-names: should be "iahb" "isfr" - reg-io-width: See dw_hdmi.txt. Shall be 4.
- rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
- interrupts: HDMI interrupt number - interrupts: HDMI interrupt number
- ports: contain a port node with endpoint definitions as defined in - clocks: See dw_hdmi.txt.
Documentation/devicetree/bindings/media/video-interfaces.txt. For - clock-names: Shall contain "iahb" and "isfr" as defined in dw_hdmi.txt.
vopb,set the reg = <0> and set the reg = <1> for vopl. - ports: See dw_hdmi.txt. The DWC HDMI shall have a single port numbered 0
- reg-io-width: the width of the reg:1,4, the value should be 4 on corresponding to the video input of the controller. The port shall have two
rk3288 platform endpoints, numbered 0 and 1, connected respectively to the vopb and vopl.
- rockchip,grf: Shall reference the GRF to mux vopl/vopb.
Optional properties 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: Example:
hdmi: hdmi@ff980000 { hdmi: hdmi@ff980000 {
compatible = "rockchip,rk3288-dw-hdmi"; compatible = "rockchip,rk3288-dw-hdmi";
reg = <0xff980000 0x20000>; reg = <0xff980000 0x20000>;

View File

@ -470,3 +470,9 @@ DRM MM Range Allocator Function References
.. kernel-doc:: include/drm/drm_mm.h .. kernel-doc:: include/drm/drm_mm.h
:internal: :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 config DRM_VM
bool bool
depends on DRM depends on DRM && MMU
source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/i2c/Kconfig"
@ -267,7 +267,7 @@ source "drivers/gpu/drm/meson/Kconfig"
menuconfig DRM_LEGACY menuconfig DRM_LEGACY
bool "Enable legacy drivers (DANGEROUS)" bool "Enable legacy drivers (DANGEROUS)"
depends on DRM depends on DRM && MMU
select DRM_VM select DRM_VM
help help
Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous

View File

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

View File

@ -12,6 +12,7 @@
#include <linux/hdmi.h> #include <linux/hdmi.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_mipi_dsi.h> #include <drm/drm_mipi_dsi.h>
@ -317,6 +318,8 @@ struct adv7511 {
bool edid_read; bool edid_read;
wait_queue_head_t wq; wait_queue_head_t wq;
struct work_struct hpd_work;
struct drm_bridge bridge; struct drm_bridge bridge;
struct drm_connector connector; struct drm_connector connector;
@ -329,6 +332,9 @@ struct adv7511 {
struct gpio_desc *gpio_pd; struct gpio_desc *gpio_pd;
struct regulator_bulk_data *supplies;
unsigned int num_supplies;
/* ADV7533 DSI RX related params */ /* ADV7533 DSI RX related params */
struct device_node *host_node; struct device_node *host_node;
struct mipi_dsi_device *dsi; 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; 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; 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. * Still, let's be safe and stick to the documentation.
*/ */
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), 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), regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
ADV7511_INT1_DDC_ERROR); ADV7511_INT1_DDC_ERROR);
} }
@ -354,6 +354,11 @@ static void adv7511_power_on(struct adv7511 *adv7511)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2, regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
ADV7511_REG_POWER2_HPD_SRC_MASK, ADV7511_REG_POWER2_HPD_SRC_MASK,
ADV7511_REG_POWER2_HPD_SRC_NONE); 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. * 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) if (adv7511->type == ADV7533)
adv7533_dsi_power_on(adv7511); adv7533_dsi_power_on(adv7511);
adv7511->powered = true; 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 */ /* TODO: setup additional power down modes */
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN); ADV7511_POWER_POWER_DOWN);
regcache_mark_dirty(adv7511->regmap); regcache_mark_dirty(adv7511->regmap);
}
static void adv7511_power_off(struct adv7511 *adv7511)
{
__adv7511_power_off(adv7511);
if (adv7511->type == ADV7533) if (adv7511->type == ADV7533)
adv7533_dsi_power_off(adv7511); adv7533_dsi_power_off(adv7511);
adv7511->powered = false; adv7511->powered = false;
} }
@ -402,6 +409,27 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
return false; 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) static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
{ {
unsigned int irq0, irq1; 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); regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder) 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) { if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
adv7511->edid_read = true; 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 */ /* Reading the EDID only works if the device is powered */
if (!adv7511->powered) { if (!adv7511->powered) {
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, unsigned int edid_i2c_addr =
ADV7511_POWER_POWER_DOWN, 0); (adv7511->i2c_main->addr << 1) + 4;
if (adv7511->i2c_main->irq) {
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), __adv7511_power_on(adv7511);
ADV7511_INT0_EDID_READY);
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), /* Reset the EDID_I2C_ADDR register as it might be cleared */
ADV7511_INT1_DDC_ERROR); regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
} edid_i2c_addr);
adv7511->current_edid_segment = -1;
} }
edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511); edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
if (!adv7511->powered) if (!adv7511->powered)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, __adv7511_power_off(adv7511);
ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN);
kfree(adv7511->edid); kfree(adv7511->edid);
adv7511->edid = edid; adv7511->edid = edid;
@ -825,6 +850,10 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
if (adv->type == ADV7533) if (adv->type == ADV7533)
ret = adv7533_attach_dsi(adv); ret = adv7533_attach_dsi(adv);
if (adv->i2c_main->irq)
regmap_write(adv->regmap, ADV7511_REG_INT_ENABLE(0),
ADV7511_INT0_HPD);
return ret; return ret;
} }
@ -839,6 +868,58 @@ static struct drm_bridge_funcs adv7511_bridge_funcs = {
* Probe & remove * 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, static int adv7511_parse_dt(struct device_node *np,
struct adv7511_link_config *config) 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) if (!adv7511)
return -ENOMEM; return -ENOMEM;
adv7511->i2c_main = i2c;
adv7511->powered = false; adv7511->powered = false;
adv7511->status = connector_status_disconnected; 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) if (ret)
return 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 * The power down GPIO is optional. If present, toggle it from active to
* inactive to wake up the encoder. * inactive to wake up the encoder.
*/ */
adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); adv7511->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH);
if (IS_ERR(adv7511->gpio_pd)) if (IS_ERR(adv7511->gpio_pd)) {
return PTR_ERR(adv7511->gpio_pd); ret = PTR_ERR(adv7511->gpio_pd);
goto uninit_regulators;
}
if (adv7511->gpio_pd) { if (adv7511->gpio_pd) {
mdelay(5); 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); adv7511->regmap = devm_regmap_init_i2c(i2c, &adv7511_regmap_config);
if (IS_ERR(adv7511->regmap)) if (IS_ERR(adv7511->regmap)) {
return PTR_ERR(adv7511->regmap); ret = PTR_ERR(adv7511->regmap);
goto uninit_regulators;
}
ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val); ret = regmap_read(adv7511->regmap, ADV7511_REG_CHIP_REVISION, &val);
if (ret) if (ret)
return ret; goto uninit_regulators;
dev_dbg(dev, "Rev. %d\n", val); dev_dbg(dev, "Rev. %d\n", val);
if (adv7511->type == ADV7511) if (adv7511->type == ADV7511)
@ -985,7 +1077,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
else else
ret = adv7533_patch_registers(adv7511); ret = adv7533_patch_registers(adv7511);
if (ret) 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_EDID_I2C_ADDR, edid_i2c_addr);
regmap_write(adv7511->regmap, ADV7511_REG_PACKET_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_packet_disable(adv7511, 0xffff);
adv7511->i2c_main = i2c;
adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); adv7511->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1);
if (!adv7511->i2c_edid) if (!adv7511->i2c_edid) {
return -ENOMEM; ret = -ENOMEM;
goto uninit_regulators;
}
if (adv7511->type == ADV7533) { if (adv7511->type == ADV7533) {
ret = adv7533_init_cec(adv7511); 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; goto err_i2c_unregister_edid;
} }
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
if (i2c->irq) { if (i2c->irq) {
init_waitqueue_head(&adv7511->wq); init_waitqueue_head(&adv7511->wq);
@ -1045,6 +1140,8 @@ err_unregister_cec:
adv7533_uninit_cec(adv7511); adv7533_uninit_cec(adv7511);
err_i2c_unregister_edid: err_i2c_unregister_edid:
i2c_unregister_device(adv7511->i2c_edid); i2c_unregister_device(adv7511->i2c_edid);
uninit_regulators:
adv7511_uninit_regulators(adv7511);
return ret; return ret;
} }
@ -1058,6 +1155,8 @@ static int adv7511_remove(struct i2c_client *i2c)
adv7533_uninit_cec(adv7511); adv7533_uninit_cec(adv7511);
} }
adv7511_uninit_regulators(adv7511);
drm_bridge_remove(&adv7511->bridge); drm_bridge_remove(&adv7511->bridge);
adv7511_audio_exit(adv7511); adv7511_audio_exit(adv7511);

View File

@ -113,13 +113,20 @@ struct dw_hdmi_i2c {
bool is_regaddr; bool is_regaddr;
}; };
struct dw_hdmi_phy_data {
enum dw_hdmi_phy_type type;
const char *name;
bool has_svsret;
};
struct dw_hdmi { struct dw_hdmi {
struct drm_connector connector; 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; struct platform_device *audio;
enum dw_hdmi_devtype dev_type;
struct device *dev; struct device *dev;
struct clk *isfr_clk; struct clk *isfr_clk;
struct clk *iahb_clk; struct clk *iahb_clk;
@ -133,7 +140,9 @@ struct dw_hdmi {
u8 edid[HDMI_EDID_LEN]; u8 edid[HDMI_EDID_LEN];
bool cable_plugin; bool cable_plugin;
const struct dw_hdmi_phy_data *phy;
bool phy_enabled; bool phy_enabled;
struct drm_display_mode previous_mode; struct drm_display_mode previous_mode;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
@ -868,7 +877,7 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
return true; 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) unsigned char addr)
{ {
hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0); 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); 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) static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
{ {
hdmi_mask_writeb(hdmi, !enable, HDMI_PHY_CONF0, 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); 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_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
HDMI_PHY_CONF0_SPARECTRL_OFFSET, HDMI_PHY_CONF0_SVSRET_OFFSET,
HDMI_PHY_CONF0_SPARECTRL_MASK); HDMI_PHY_CONF0_SVSRET_MASK);
} }
static void dw_hdmi_phy_gen2_pddq(struct dw_hdmi *hdmi, u8 enable) 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); HDMI_PHY_CONF0_SELDIPIF_MASK);
} }
static int hdmi_phy_configure(struct dw_hdmi *hdmi, unsigned char prep, static int hdmi_phy_configure(struct dw_hdmi *hdmi, int cscon)
unsigned char res, int cscon)
{ {
unsigned res_idx;
u8 val, msec; u8 val, msec;
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data; 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_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config; 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 */ /* PLL/MPLL Cfg - always match on final entry */
for (; mpll_config->mpixelclock != ~0UL; mpll_config++) for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
if (hdmi->hdmi_data.video_mode.mpixelclock <= 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 */ /* gen2 pddq */
dw_hdmi_phy_gen2_pddq(hdmi, 1); dw_hdmi_phy_gen2_pddq(hdmi, 1);
/* PHY reset */ /* Leave low power consumption mode by asserting SVSRET. */
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); if (hdmi->phy->has_svsret)
hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); 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); 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_I2CM_SLAVE_ADDR);
hdmi_phy_test_clear(hdmi, 0); 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[0].cpce,
hdmi_phy_i2c_write(hdmi, mpll_config->res[res_idx].gmp, 0x15); 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, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[res_idx], 0x10); 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, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); 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 */ /* Override and disable clock termination. */
hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */ hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */ HDMI_3D_TX_PHY_CKCALCTRL);
/* REMOVE CLK TERM */
hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
dw_hdmi_phy_enable_powerdown(hdmi, false); 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_txpwron(hdmi, 1);
dw_hdmi_phy_gen2_pddq(hdmi, 0); dw_hdmi_phy_gen2_pddq(hdmi, 0);
if (hdmi->dev_type == RK3288_HDMI) /* Wait for PHY PLL lock */
dw_hdmi_phy_enable_spare(hdmi, 1);
/*Wait for PHY PLL lock */
msec = 5; msec = 5;
do { do {
val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK; 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); dw_hdmi_phy_enable_powerdown(hdmi, true);
/* Enable CSC */ /* Enable CSC */
ret = hdmi_phy_configure(hdmi, 0, 8, cscon); ret = hdmi_phy_configure(hdmi, cscon);
if (ret) if (ret)
return ret; return ret;
} }
@ -1351,19 +1339,38 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
/* Workaround to clear the overflow condition */ /* Workaround to clear the overflow condition */
static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi) static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
{ {
int count; unsigned int count;
unsigned int i;
u8 val; 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 */ /* TMDS software reset */
hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF); val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
if (hdmi->dev_type == IMX6DL_HDMI) { for (i = 0; i < count; i++)
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
return;
}
for (count = 0; count < 4; count++)
hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF); 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); 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 static enum drm_connector_status
dw_hdmi_connector_detect(struct drm_connector *connector, bool force) 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, .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 = { static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.attach = dw_hdmi_bridge_attach,
.enable = dw_hdmi_bridge_enable, .enable = dw_hdmi_bridge_enable,
.disable = dw_hdmi_bridge_disable, .disable = dw_hdmi_bridge_disable,
.mode_set = dw_hdmi_bridge_mode_set, .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) { if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
dev_dbg(hdmi->dev, "EVENT=%s\n", dev_dbg(hdmi->dev, "EVENT=%s\n",
phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout"); 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); 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; 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; unsigned int i;
struct drm_bridge *bridge; u8 phy_type;
int ret;
bridge = devm_kzalloc(drm->dev, sizeof(*bridge), GFP_KERNEL); phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
if (!bridge) {
DRM_ERROR("Failed to allocate drm bridge\n"); for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
return -ENOMEM; if (dw_hdmi_phys[i].type == phy_type) {
hdmi->phy = &dw_hdmi_phys[i];
return 0;
}
} }
hdmi->bridge = bridge; if (phy_type == DW_HDMI_PHY_VENDOR_PHY)
bridge->driver_private = hdmi; dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n");
bridge->funcs = &dw_hdmi_bridge_funcs; else
ret = drm_bridge_attach(encoder, bridge, NULL); dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n",
if (ret) { phy_type);
DRM_ERROR("Failed to initialize bridge with drm\n");
return -EINVAL;
}
hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; return -ENODEV;
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;
} }
int dw_hdmi_bind(struct device *dev, struct device *master, static struct dw_hdmi *
void *data, struct drm_encoder *encoder, __dw_hdmi_probe(struct platform_device *pdev,
struct resource *iores, int irq, const struct dw_hdmi_plat_data *plat_data)
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 device_node *np = dev->of_node;
struct platform_device_info pdevinfo; struct platform_device_info pdevinfo;
struct device_node *ddc_node; struct device_node *ddc_node;
struct dw_hdmi *hdmi; struct dw_hdmi *hdmi;
struct resource *iores;
int irq;
int ret; int ret;
u32 val = 1; u32 val = 1;
u8 prod_id0;
u8 prod_id1;
u8 config0; u8 config0;
u8 config1; u8 config3;
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi) if (!hdmi)
return -ENOMEM; return ERR_PTR(-ENOMEM);
hdmi->connector.interlace_allowed = 1;
hdmi->plat_data = plat_data; hdmi->plat_data = plat_data;
hdmi->dev = dev; hdmi->dev = dev;
hdmi->dev_type = plat_data->dev_type; hdmi->dev_type = plat_data->dev_type;
hdmi->sample_rate = 48000; hdmi->sample_rate = 48000;
hdmi->encoder = encoder;
hdmi->disabled = true; hdmi->disabled = true;
hdmi->rxsense = true; hdmi->rxsense = true;
hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE); 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; break;
default: default:
dev_err(dev, "reg-io-width must be 1 or 4\n"); 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); 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); of_node_put(ddc_node);
if (!hdmi->ddc) { if (!hdmi->ddc) {
dev_dbg(hdmi->dev, "failed to read ddc node\n"); dev_dbg(hdmi->dev, "failed to read ddc node\n");
return -EPROBE_DEFER; return ERR_PTR(-EPROBE_DEFER);
} }
} else { } else {
dev_dbg(hdmi->dev, "no ddc property found\n"); dev_dbg(hdmi->dev, "no ddc property found\n");
} }
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hdmi->regs = devm_ioremap_resource(dev, iores); hdmi->regs = devm_ioremap_resource(dev, iores);
if (IS_ERR(hdmi->regs)) { if (IS_ERR(hdmi->regs)) {
ret = PTR_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 */ /* Product and revision IDs */
dev_info(dev, hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", | (hdmi_readb(hdmi, HDMI_REVISION_ID) << 0);
hdmi_readb(hdmi, HDMI_DESIGN_ID), prod_id0 = hdmi_readb(hdmi, HDMI_PRODUCT_ID0);
hdmi_readb(hdmi, HDMI_REVISION_ID), prod_id1 = hdmi_readb(hdmi, HDMI_PRODUCT_ID1);
hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
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); 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, ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
dw_hdmi_irq, IRQF_SHARED, dw_hdmi_irq, IRQF_SHARED,
dev_name(dev), hdmi); 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_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
HDMI_IH_PHY_STAT0); HDMI_IH_PHY_STAT0);
ret = dw_hdmi_fb_registered(hdmi); hdmi->bridge.driver_private = hdmi;
if (ret) hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
goto err_iahb; hdmi->bridge.of_node = pdev->dev.of_node;
ret = dw_hdmi_register(drm, hdmi); ret = dw_hdmi_fb_registered(hdmi);
if (ret) if (ret)
goto err_iahb; goto err_iahb;
@ -2012,9 +2075,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
pdevinfo.id = PLATFORM_DEVID_AUTO; pdevinfo.id = PLATFORM_DEVID_AUTO;
config0 = hdmi_readb(hdmi, HDMI_CONFIG0_ID); 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; struct dw_hdmi_audio_data audio;
audio.phys = iores->start; audio.phys = iores->start;
@ -2046,9 +2109,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
if (hdmi->i2c) if (hdmi->i2c)
dw_hdmi_i2c_init(hdmi); dw_hdmi_i2c_init(hdmi);
dev_set_drvdata(dev, hdmi); platform_set_drvdata(pdev, hdmi);
return 0; return hdmi;
err_iahb: err_iahb:
if (hdmi->i2c) { if (hdmi->i2c) {
@ -2062,14 +2125,11 @@ err_isfr:
err_res: err_res:
i2c_put_adapter(hdmi->ddc); 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)) if (hdmi->audio && !IS_ERR(hdmi->audio))
platform_device_unregister(hdmi->audio); platform_device_unregister(hdmi->audio);
@ -2084,6 +2144,70 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
else else
i2c_put_adapter(hdmi->ddc); 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); EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");

View File

@ -545,12 +545,24 @@
#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12
enum { 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 */ /* CONFIG0_ID field values */
HDMI_CONFIG0_I2S = 0x10, HDMI_CONFIG0_I2S = 0x10,
/* CONFIG1_ID field values */ /* CONFIG1_ID field values */
HDMI_CONFIG1_AHB = 0x01, HDMI_CONFIG1_AHB = 0x01,
/* CONFIG3_ID field values */
HDMI_CONFIG3_AHBAUDDMA = 0x02,
HDMI_CONFIG3_GPAUD = 0x01,
/* IH_FC_INT2 field values */ /* IH_FC_INT2 field values */
HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03,
HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02,
@ -847,8 +859,8 @@ enum {
HDMI_PHY_CONF0_PDZ_OFFSET = 7, HDMI_PHY_CONF0_PDZ_OFFSET = 7,
HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40,
HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, HDMI_PHY_CONF0_ENTMDS_OFFSET = 6,
HDMI_PHY_CONF0_SPARECTRL_MASK = 0x20, HDMI_PHY_CONF0_SVSRET_MASK = 0x20,
HDMI_PHY_CONF0_SPARECTRL_OFFSET = 5, HDMI_PHY_CONF0_SVSRET_OFFSET = 5,
HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10,
HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4,
HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8,
@ -977,8 +989,7 @@ enum {
HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0,
/* MC_PHYRSTZ field values */ /* MC_PHYRSTZ field values */
HDMI_MC_PHYRSTZ_ASSERT = 0x0, HDMI_MC_PHYRSTZ_PHYRSTZ = 0x01,
HDMI_MC_PHYRSTZ_DEASSERT = 0x1,
/* MC_HEACPHY_RST field values */ /* MC_HEACPHY_RST field values */
HDMI_MC_HEACPHY_RST_ASSERT = 0x1, HDMI_MC_HEACPHY_RST_ASSERT = 0x1,
@ -1073,4 +1084,70 @@ enum {
HDMI_I2CM_CTLINT_ARB_MASK = 0x4, 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__ */ #endif /* __DW_HDMI_H__ */

View File

@ -2708,6 +2708,44 @@ backoff:
} }
EXPORT_SYMBOL(drm_atomic_helper_connector_set_property); 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 * drm_atomic_helper_page_flip - execute a legacy page flip
* @crtc: DRM crtc * @crtc: DRM crtc
@ -2715,7 +2753,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_set_property);
* @event: optional DRM event to signal upon completion * @event: optional DRM event to signal upon completion
* @flags: flip flags for non-vblank sync'ed updates * @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 * 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 * 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:
* Returns 0 on success, negative errno numbers on failure. * 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, int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb, 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_plane *plane = crtc->primary;
struct drm_atomic_state *state; struct drm_atomic_state *state;
struct drm_plane_state *plane_state;
struct drm_crtc_state *crtc_state;
int ret = 0; int ret = 0;
if (flags & DRM_MODE_PAGE_FLIP_ASYNC) if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
@ -2743,35 +2783,14 @@ int drm_atomic_helper_page_flip(struct drm_crtc *crtc,
return -ENOMEM; return -ENOMEM;
state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
retry: retry:
crtc_state = drm_atomic_get_crtc_state(state, crtc); ret = page_flip_common(state, crtc, fb, event);
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);
if (ret != 0) if (ret != 0)
goto fail; 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); ret = drm_atomic_nonblocking_commit(state);
fail: fail:
if (ret == -EDEADLK) if (ret == -EDEADLK)
goto backoff; goto backoff;
@ -2794,6 +2813,78 @@ backoff:
} }
EXPORT_SYMBOL(drm_atomic_helper_page_flip); 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 * drm_atomic_helper_connector_dpms() - connector dpms helper implementation
* @connector: affected connector * @connector: affected connector

View File

@ -67,6 +67,14 @@ static void drm_cache_flush_clflush(struct page *pages[],
} }
#endif #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 void
drm_clflush_pages(struct page *pages[], unsigned long num_pages) 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); 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 void
drm_clflush_sg(struct sg_table *st) drm_clflush_sg(struct sg_table *st)
{ {
@ -125,6 +140,14 @@ drm_clflush_sg(struct sg_table *st)
} }
EXPORT_SYMBOL(drm_clflush_sg); 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 void
drm_clflush_virt_range(void *addr, unsigned long length) 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; return 0;
err_cma_destroy: err_cma_destroy:
drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); drm_framebuffer_remove(&fbdev_cma->fb->fb);
drm_fb_cma_destroy(&fbdev_cma->fb->fb);
err_fb_info_destroy: err_fb_info_destroy:
drm_fb_helper_release_fbi(helper); drm_fb_helper_release_fbi(helper);
err_gem_free_object: 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_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev);
drm_fb_helper_release_fbi(&fbdev_cma->fb_helper); drm_fb_helper_release_fbi(&fbdev_cma->fb_helper);
if (fbdev_cma->fb) { if (fbdev_cma->fb)
drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); drm_framebuffer_remove(&fbdev_cma->fb->fb);
drm_fb_cma_destroy(&fbdev_cma->fb->fb);
}
drm_fb_helper_fini(&fbdev_cma->fb_helper); drm_fb_helper_fini(&fbdev_cma->fb_helper);
kfree(fbdev_cma); kfree(fbdev_cma);

View File

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

View File

@ -467,7 +467,7 @@ err:
clk_disable_unprepare(ctx->clks[i]); 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, .enable = decon_enable,
.disable = decon_disable, .disable = decon_disable,
.enable_vblank = decon_enable_vblank, .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) static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{ {
struct exynos_drm_crtc *exynos_crtc = to_exynos_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); drm_crtc_cleanup(crtc);
kfree(exynos_crtc); kfree(exynos_crtc);
@ -134,7 +131,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
void *ctx) void *ctx)
{ {
struct exynos_drm_crtc *exynos_crtc; struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = drm_dev->dev_private;
struct drm_crtc *crtc; struct drm_crtc *crtc;
int ret; int ret;
@ -149,8 +145,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
crtc = &exynos_crtc->base; crtc = &exynos_crtc->base;
private->crtc[pipe] = crtc;
ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
&exynos_crtc_funcs, NULL); &exynos_crtc_funcs, NULL);
if (ret < 0) if (ret < 0)

View File

@ -211,12 +211,6 @@ struct drm_exynos_file_private {
struct exynos_drm_private { struct exynos_drm_private {
struct drm_fb_helper *fb_helper; 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; struct device *dma_dev;
void *mapping; void *mapping;
@ -231,9 +225,9 @@ struct exynos_drm_private {
static inline struct exynos_drm_crtc * static inline struct exynos_drm_crtc *
exynos_drm_crtc_from_pipe(struct drm_device *dev, int pipe) 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) 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) static int ade_enable_vblank(struct drm_device *dev, unsigned int pipe)
{ {
struct kirin_drm_private *priv = dev->dev_private; struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx; struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base; 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) static void ade_disable_vblank(struct drm_device *dev, unsigned int pipe)
{ {
struct kirin_drm_private *priv = dev->dev_private; struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe);
struct ade_crtc *acrtc = to_ade_crtc(priv->crtc[pipe]); struct ade_crtc *acrtc = to_ade_crtc(crtc);
struct ade_hw_ctx *ctx = acrtc->ctx; struct ade_hw_ctx *ctx = acrtc->ctx;
void __iomem *base = ctx->base; 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, static int ade_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *plane) struct drm_plane *plane)
{ {
struct kirin_drm_private *priv = dev->dev_private;
struct device_node *port; struct device_node *port;
int ret; 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); drm_crtc_helper_add(crtc, &ade_crtc_helper_funcs);
priv->crtc[drm_crtc_index(crtc)] = crtc;
return 0; return 0;
} }

View File

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

View File

@ -49,6 +49,7 @@
#include <drm/drm_legacy.h> /* for struct drm_dma_handle */ #include <drm/drm_legacy.h> /* for struct drm_dma_handle */
#include <drm/drm_gem.h> #include <drm/drm_gem.h>
#include <drm/drm_auth.h> #include <drm/drm_auth.h>
#include <drm/drm_cache.h>
#include "i915_params.h" #include "i915_params.h"
#include "i915_reg.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_device *drm = data;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct imx_hdmi *hdmi; struct imx_hdmi *hdmi;
struct resource *iores;
int irq;
int ret; int ret;
if (!pdev->dev.of_node) 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; hdmi->dev = &pdev->dev;
encoder = &hdmi->encoder; 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); encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
/* /*
* If we failed to find the CRTC(s) which this encoder is * 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_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL); 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(), * 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, static void dw_hdmi_imx_unbind(struct device *dev, struct device *master,
void *data) void *data)
{ {
return dw_hdmi_unbind(dev, master, data); return dw_hdmi_unbind(dev);
} }
static const struct component_ops dw_hdmi_imx_ops = { 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) int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
{ {
struct mtk_drm_private *priv = drm->dev_private; struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
mtk_ddp_comp_enable_vblank(ovl, &mtk_crtc->base); 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) void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
{ {
struct mtk_drm_private *priv = drm->dev_private; struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(priv->crtc[pipe]); struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0]; struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
mtk_ddp_comp_disable_vblank(ovl); mtk_ddp_comp_disable_vblank(ovl);
@ -588,7 +588,6 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
goto unprepare; goto unprepare;
drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE); 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); drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE);
priv->crtc[pipe] = &mtk_crtc->base;
priv->num_pipes++; priv->num_pipes++;
return 0; return 0;

View File

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

View File

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

View File

@ -174,10 +174,8 @@ fail_unlock:
fail: fail:
if (ret) { if (ret) {
if (fb) { if (fb)
drm_framebuffer_unregister_private(fb);
drm_framebuffer_remove(fb); drm_framebuffer_remove(fb);
}
} }
return ret; return ret;
@ -247,7 +245,6 @@ void msm_fbdev_free(struct drm_device *dev)
/* this will free the backing object */ /* this will free the backing object */
if (fbdev->fb) { if (fbdev->fb) {
msm_gem_put_vaddr(fbdev->bo); msm_gem_put_vaddr(fbdev->bo);
drm_framebuffer_unregister_private(fbdev->fb);
drm_framebuffer_remove(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) nouveau_display_vblank_enable(struct drm_device *dev, unsigned int pipe)
{ {
struct drm_crtc *crtc; struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
if (nv_crtc->index == pipe) { crtc = drm_crtc_from_index(dev, pipe);
nvif_notify_get(&nv_crtc->vblank); if (!crtc)
return 0; return -EINVAL;
}
} nv_crtc = nouveau_crtc(crtc);
return -EINVAL; nvif_notify_get(&nv_crtc->vblank);
return 0;
} }
void void
nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe) nouveau_display_vblank_disable(struct drm_device *dev, unsigned int pipe)
{ {
struct drm_crtc *crtc; struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
if (nv_crtc->index == pipe) { crtc = drm_crtc_from_index(dev, pipe);
nvif_notify_put(&nv_crtc->vblank); if (!crtc)
return; return;
}
} nv_crtc = nouveau_crtc(crtc);
nvif_notify_put(&nv_crtc->vblank);
} }
static inline int static inline int

View File

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

View File

@ -84,8 +84,18 @@ int
qxl_debugfs_init(struct drm_minor *minor) qxl_debugfs_init(struct drm_minor *minor)
{ {
#if defined(CONFIG_DEBUG_FS) #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, drm_debugfs_create_files(qxl_debugfs_list, QXL_DEBUGFS_ENTRIES,
minor->debugfs_root, minor); minor->debugfs_root, minor);
r = qxl_ttm_debugfs_init(dev);
if (r) {
DRM_ERROR("Failed to init TTM debugfs\n");
return r;
}
#endif #endif
return 0; 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); dev->mode_config.suggested_x_property, 0);
drm_object_attach_property(&connector->base, drm_object_attach_property(&connector->base,
dev->mode_config.suggested_y_property, 0); dev->mode_config.suggested_y_property, 0);
drm_connector_register(connector);
return 0; return 0;
} }

View File

@ -62,20 +62,83 @@ static struct pci_driver qxl_pci_driver;
static int static int
qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 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) { if (pdev->revision < 4) {
DRM_ERROR("qxl too old, doesn't support client_monitors_config," DRM_ERROR("qxl too old, doesn't support client_monitors_config,"
" use xf86-video-qxl in user mode"); " use xf86-video-qxl in user mode");
return -EINVAL; /* TODO: ENODEV ? */ 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 static void
qxl_pci_remove(struct pci_dev *pdev) qxl_pci_remove(struct pci_dev *pdev)
{ {
struct drm_device *dev = pci_get_drvdata(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 = { static const struct file_operations qxl_fops = {
@ -230,8 +293,6 @@ static struct pci_driver qxl_pci_driver = {
static struct drm_driver qxl_driver = { static struct drm_driver qxl_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
.load = qxl_driver_load,
.unload = qxl_driver_unload,
.get_vblank_counter = qxl_noop_get_vblank_counter, .get_vblank_counter = qxl_noop_get_vblank_counter,
.enable_vblank = qxl_noop_enable_vblank, .enable_vblank = qxl_noop_enable_vblank,
.disable_vblank = qxl_noop_disable_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 const struct drm_ioctl_desc qxl_ioctls[];
extern int qxl_max_ioctl; extern int qxl_max_ioctl;
int qxl_driver_load(struct drm_device *dev, unsigned long flags); int qxl_device_init(struct qxl_device *qdev, struct drm_device *ddev,
void qxl_driver_unload(struct drm_device *dev); struct pci_dev *pdev, unsigned long flags);
void qxl_device_fini(struct qxl_device *qdev);
int qxl_modeset_init(struct qxl_device *qdev); int qxl_modeset_init(struct qxl_device *qdev);
void qxl_modeset_fini(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); int qxl_debugfs_init(struct drm_minor *minor);
void qxl_debugfs_takedown(struct drm_minor *minor); void qxl_debugfs_takedown(struct drm_minor *minor);
int qxl_ttm_debugfs_init(struct qxl_device *qdev);
/* qxl_prime.c */ /* qxl_prime.c */
int qxl_gem_prime_pin(struct drm_gem_object *obj); 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); 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 drm_device *ddev,
struct pci_dev *pdev, struct pci_dev *pdev,
unsigned long flags) unsigned long flags)
@ -263,7 +263,7 @@ static int qxl_device_init(struct qxl_device *qdev,
return 0; 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]) if (qdev->current_release_bo[0])
qxl_bo_unref(&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; qdev->mode_info.num_modes = 0;
qxl_debugfs_remove_files(qdev); 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 "qxl_object.h"
#include <linux/delay.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) 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)); ((unsigned)num_io_pages * PAGE_SIZE) / (1024 * 1024));
DRM_INFO("qxl: %uM of Surface memory size\n", DRM_INFO("qxl: %uM of Surface memory size\n",
(unsigned)qdev->surfaceram_size / (1024 * 1024)); (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; return 0;
} }
@ -473,7 +467,7 @@ static int qxl_mm_dump_table(struct seq_file *m, void *data)
} }
#endif #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) #if defined(CONFIG_DEBUG_FS)
static struct drm_info_list qxl_mem_types_list[QXL_DEBUGFS_MEM_TYPES]; 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_device *drm = data;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi; struct rockchip_hdmi *hdmi;
struct resource *iores;
int irq;
int ret; int ret;
if (!pdev->dev.of_node) 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; hdmi->dev = &pdev->dev;
encoder = &hdmi->encoder; 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); encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
/* /*
* If we failed to find the CRTC(s) which this encoder is * 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_encoder_init(drm, encoder, &dw_hdmi_rockchip_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL); 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(), * 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, static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
void *data) void *data)
{ {
return dw_hdmi_unbind(dev, master, data); return dw_hdmi_unbind(dev);
} }
static const struct component_ops dw_hdmi_rockchip_ops = { 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)) if (drm_mm_scan_add_block(&scan, &e->node))
break; break;
} }
err = 0;
list_for_each_entry(e, &evict_list, link) { list_for_each_entry(e, &evict_list, link) {
if (!drm_mm_scan_remove_block(&scan, &e->node)) { if (!drm_mm_scan_remove_block(&scan, &e->node)) {
pr_err("Node %lld not marked for eviction!\n", if (!err) {
e->node.start); pr_err("Node %lld not marked for eviction!\n",
list_del(&e->link); e->node.start);
err = -EINVAL;
}
} }
} }
if (err)
return false;
list_for_each_entry(e, &evict_list, link) list_for_each_entry(e, &evict_list, link)
drm_mm_remove_node(&e->node); drm_mm_remove_node(&e->node);

View File

@ -804,23 +804,10 @@ static const struct file_operations tegra_drm_fops = {
.llseek = noop_llseek, .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, static u32 tegra_drm_get_vblank_counter(struct drm_device *drm,
unsigned int pipe) 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); struct tegra_dc *dc = to_tegra_dc(crtc);
if (!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) 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); struct tegra_dc *dc = to_tegra_dc(crtc);
if (!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) 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); struct tegra_dc *dc = to_tegra_dc(crtc);
if (crtc) if (crtc)

View File

@ -271,8 +271,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
return 0; return 0;
destroy: destroy:
drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb);
tegra_fb_destroy(fb);
release: release:
drm_fb_helper_release_fbi(helper); drm_fb_helper_release_fbi(helper);
return err; 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_unregister_fbi(&fbdev->base);
drm_fb_helper_release_fbi(&fbdev->base); drm_fb_helper_release_fbi(&fbdev->base);
if (fbdev->fb) { if (fbdev->fb)
drm_framebuffer_unregister_private(&fbdev->fb->base);
drm_framebuffer_remove(&fbdev->fb->base); drm_framebuffer_remove(&fbdev->fb->base);
}
drm_fb_helper_fini(&fbdev->base); drm_fb_helper_fini(&fbdev->base);
tegra_fbdev_free(fbdev); 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) const struct drm_display_mode *mode)
{ {
struct vc4_dev *vc4 = to_vc4_dev(dev); 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; u32 val;
int fifo_lines; int fifo_lines;
int vblank_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, int *max_error, struct timeval *vblank_time,
unsigned flags) unsigned flags)
{ {
struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
struct drm_crtc *crtc = &vc4_crtc->base;
struct drm_crtc_state *state = crtc->state; struct drm_crtc_state *state = crtc->state;
/* Helper routine in DRM core does all the work: */ /* 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) int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id)
{ {
struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
CRTC_WRITE(PV_INTEN, PV_INT_VFP_START); 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) void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id)
{ {
struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
CRTC_WRITE(PV_INTEN, 0); 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 platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master); struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_crtc *vc4_crtc; struct vc4_crtc *vc4_crtc;
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_plane *primary_plane, *cursor_plane, *destroy_plane, *temp; 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); &vc4_crtc_funcs, NULL);
drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
primary_plane->crtc = crtc; primary_plane->crtc = crtc;
vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
vc4_crtc->channel = vc4_crtc->data->hvs_channel; vc4_crtc->channel = vc4_crtc->data->hvs_channel;
drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); 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_hdmi *hdmi;
struct vc4_hvs *hvs; struct vc4_hvs *hvs;
struct vc4_crtc *crtc[3];
struct vc4_v3d *v3d; struct vc4_v3d *v3d;
struct vc4_dpi *dpi; struct vc4_dpi *dpi;
struct vc4_vec *vec; struct vc4_vec *vec;

View File

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

View File

@ -27,6 +27,16 @@ enum dw_hdmi_devtype {
RK3288_HDMI, 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 { struct dw_hdmi_mpll_config {
unsigned long mpixelclock; unsigned long mpixelclock;
struct { struct {
@ -56,10 +66,11 @@ struct dw_hdmi_plat_data {
struct drm_display_mode *mode); struct drm_display_mode *mode);
}; };
void dw_hdmi_unbind(struct device *dev, struct device *master, void *data); int dw_hdmi_probe(struct platform_device *pdev,
int dw_hdmi_bind(struct device *dev, struct device *master, const struct dw_hdmi_plat_data *plat_data);
void *data, struct drm_encoder *encoder, void dw_hdmi_remove(struct platform_device *pdev);
struct resource *iores, int irq, 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); const struct dw_hdmi_plat_data *plat_data);
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); 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, int drm_invalid_op(struct drm_device *dev, void *data,
struct drm_file *file_priv); 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 * These are exported to drivers so that they can implement fencing using
* DMA quiscent + idle. DMA quiescent usually requires the hardware lock. * 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_framebuffer *fb,
struct drm_pending_vblank_event *event, struct drm_pending_vblank_event *event,
uint32_t flags); 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 drm_atomic_helper_connector_dpms(struct drm_connector *connector,
int mode); int mode);
struct drm_encoder * struct drm_encoder *

View File

@ -33,7 +33,11 @@
#ifndef _DRM_CACHE_H_ #ifndef _DRM_CACHE_H_
#define _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_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) 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 *ctm;
struct drm_property_blob *gamma_lut; struct drm_property_blob *gamma_lut;
/**
* @target_vblank:
*
* Target vertical blank period when a page flip
* should take effect.
*/
u32 target_vblank;
/** /**
* @event: * @event:
* *

View File

@ -41,10 +41,17 @@ extern "C" {
/* 8 bpp Red */ /* 8 bpp Red */
#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ #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 */ /* 16 bpp RG */
#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ #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 */ #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 */ /* 8 bpp RGB */
#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ #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 */ #define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */