Merge branch 'drm_bridge_for_4.8' of https://github.com/boddob/linux into drm-next
This is an update to the previous drm bridge pull request. The ADV7511 driver's conversion from slave encoder to bridge meant that its users (the rcar-du kms driver) should use the bridge interface too. This pull request now also contains a commit that updates the rcar-du's hdmi encoder interface from slave encoder to bridge. The other updates are as before: - Converts the ADV7511 i2c slave encoder driver to a bridge driver. Adds support for the ADV7533 bridge chip. - Add bridge driver for TC358767 (DSI/DPI to eDP) encoder chips. * 'drm_bridge_for_4.8' of https://github.com/boddob/linux: drm: rcar-du: Remove i2c slave encoder interface for hdmi encoder drm/bridge: tc358767: Add DPI to eDP bridge driver dt-bindings: tc358767: add DT documentation dt-bindings: drm/bridge: Update bindings for ADV7533 drm/bridge: adv7533: Change number of DSI lanes dynamically drm/bridge: adv7533: Use internal timing generator drm/bridge: adv7533: Create a MIPI DSI device drm/bridge: adv7533: Initial support for ADV7533 drm/bridge: adv7511: Fix mutex deadlock when interrupts are disabled drm/i2c: adv7511: Move to bridge folder drm/i2c: adv7511: Convert to drm_bridge
This commit is contained in:
commit
0cf0903154
|
@ -1,13 +1,19 @@
|
|||
Analog Device ADV7511(W)/13 HDMI Encoders
|
||||
Analog Device ADV7511(W)/13/33 HDMI Encoders
|
||||
-----------------------------------------
|
||||
|
||||
The ADV7511, ADV7511W and ADV7513 are HDMI audio and video transmitters
|
||||
The ADV7511, ADV7511W, ADV7513 and ADV7533 are HDMI audio and video transmitters
|
||||
compatible with HDMI 1.4 and DVI 1.0. They support color space conversion,
|
||||
S/PDIF, CEC and HDCP.
|
||||
S/PDIF, CEC and HDCP. ADV7533 supports the DSI interface for input pixels, while
|
||||
the others support RGB interface.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of "adi,adv7511", "adi,adv7511w" or "adi,adv7513"
|
||||
- compatible: Should be one of:
|
||||
"adi,adv7511"
|
||||
"adi,adv7511w"
|
||||
"adi,adv7513"
|
||||
"adi,adv7533"
|
||||
|
||||
- reg: I2C slave address
|
||||
|
||||
The ADV7511 supports a large number of input data formats that differ by their
|
||||
|
@ -32,6 +38,11 @@ The following input format properties are required except in "rgb 1x" and
|
|||
- adi,input-justification: The input bit justification ("left", "evenly",
|
||||
"right").
|
||||
|
||||
The following properties are required for ADV7533:
|
||||
|
||||
- adi,dsi-lanes: Number of DSI data lanes connected to the DSI host. It should
|
||||
be one of 1, 2, 3 or 4.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts: Specifier for the ADV7511 interrupt
|
||||
|
@ -42,13 +53,18 @@ Optional properties:
|
|||
- adi,embedded-sync: The input uses synchronization signals embedded in the
|
||||
data stream (similar to BT.656). Defaults to separate H/V synchronization
|
||||
signals.
|
||||
- adi,disable-timing-generator: Only for ADV7533. Disables the internal timing
|
||||
generator. The chip will rely on the sync signals in the DSI data lanes,
|
||||
rather than generate its own timings for HDMI output.
|
||||
|
||||
Required nodes:
|
||||
|
||||
The ADV7511 has two video ports. Their connections are modelled using the OF
|
||||
graph bindings specified in Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
- Video port 0 for the RGB or YUV input
|
||||
- Video port 0 for the RGB, YUV or DSI input. In the case of ADV7533, the
|
||||
remote endpoint phandle should be a reference to a valid mipi_dsi_host device
|
||||
node.
|
||||
- Video port 1 for the HDMI output
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
Toshiba TC358767 eDP bridge bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: "toshiba,tc358767"
|
||||
- reg: i2c address of the bridge, 0x68 or 0x0f, depending on bootstrap pins
|
||||
- clock-names: should be "ref"
|
||||
- clocks: OF device-tree clock specification for refclk input. The reference
|
||||
clock rate must be 13 MHz, 19.2 MHz, 26 MHz, or 38.4 MHz.
|
||||
|
||||
Optional properties:
|
||||
- shutdown-gpios: OF device-tree gpio specification for SD pin
|
||||
(active high shutdown input)
|
||||
- reset-gpios: OF device-tree gpio specification for RSTX pin
|
||||
(active low system reset)
|
||||
- ports: the ports node can contain video interface port nodes to connect
|
||||
to a DPI/DSI source and to an eDP/DP sink according to [1][2]:
|
||||
- port@0: DSI input port
|
||||
- port@1: DPI input port
|
||||
- port@2: eDP/DP output port
|
||||
|
||||
[1]: Documentation/devicetree/bindings/graph.txt
|
||||
[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
|
||||
|
||||
Example:
|
||||
edp-bridge@68 {
|
||||
compatible = "toshiba,tc358767";
|
||||
reg = <0x68>;
|
||||
shutdown-gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio3 24 GPIO_ACTIVE_LOW>;
|
||||
clock-names = "ref";
|
||||
clocks = <&edp_refclk>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
bridge_in: endpoint {
|
||||
remote-endpoint = <&dpi_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
bridge_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -58,6 +58,17 @@ config DRM_SII902X
|
|||
---help---
|
||||
Silicon Image sii902x bridge chip driver.
|
||||
|
||||
config DRM_TOSHIBA_TC358767
|
||||
tristate "Toshiba TC358767 eDP bridge"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
select DRM_PANEL
|
||||
---help---
|
||||
Toshiba TC358767 eDP bridge chip driver.
|
||||
|
||||
source "drivers/gpu/drm/bridge/analogix/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/bridge/adv7511/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -6,4 +6,6 @@ obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
|
|||
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
|
||||
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
|
||||
obj-$(CONFIG_DRM_SII902X) += sii902x.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
|
||||
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
config DRM_I2C_ADV7511
|
||||
tristate "AV7511 encoder"
|
||||
depends on OF
|
||||
select DRM_KMS_HELPER
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders.
|
||||
|
||||
config DRM_I2C_ADV7533
|
||||
bool "ADV7533 encoder"
|
||||
depends on DRM_I2C_ADV7511
|
||||
select DRM_MIPI_DSI
|
||||
default y
|
||||
help
|
||||
Support for the Analog Devices ADV7533 DSI to HDMI encoder.
|
|
@ -0,0 +1,3 @@
|
|||
adv7511-y := adv7511_drv.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
|
|
@ -10,6 +10,11 @@
|
|||
#define __DRM_I2C_ADV7511_H__
|
||||
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
#define ADV7511_REG_CHIP_REVISION 0x00
|
||||
#define ADV7511_REG_N0 0x01
|
||||
|
@ -286,4 +291,102 @@ struct adv7511_video_config {
|
|||
struct hdmi_avi_infoframe avi_infoframe;
|
||||
};
|
||||
|
||||
enum adv7511_type {
|
||||
ADV7511,
|
||||
ADV7533,
|
||||
};
|
||||
|
||||
struct adv7511 {
|
||||
struct i2c_client *i2c_main;
|
||||
struct i2c_client *i2c_edid;
|
||||
struct i2c_client *i2c_cec;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap *regmap_cec;
|
||||
enum drm_connector_status status;
|
||||
bool powered;
|
||||
|
||||
struct drm_display_mode curr_mode;
|
||||
|
||||
unsigned int f_tmds;
|
||||
|
||||
unsigned int current_edid_segment;
|
||||
uint8_t edid_buf[256];
|
||||
bool edid_read;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
struct drm_bridge bridge;
|
||||
struct drm_connector connector;
|
||||
|
||||
bool embedded_sync;
|
||||
enum adv7511_sync_polarity vsync_polarity;
|
||||
enum adv7511_sync_polarity hsync_polarity;
|
||||
bool rgb;
|
||||
|
||||
struct edid *edid;
|
||||
|
||||
struct gpio_desc *gpio_pd;
|
||||
|
||||
/* ADV7533 DSI RX related params */
|
||||
struct device_node *host_node;
|
||||
struct mipi_dsi_device *dsi;
|
||||
u8 num_dsi_lanes;
|
||||
bool use_timing_gen;
|
||||
|
||||
enum adv7511_type type;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7533
|
||||
void adv7533_dsi_power_on(struct adv7511 *adv);
|
||||
void adv7533_dsi_power_off(struct adv7511 *adv);
|
||||
void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode);
|
||||
int adv7533_patch_registers(struct adv7511 *adv);
|
||||
void adv7533_uninit_cec(struct adv7511 *adv);
|
||||
int adv7533_init_cec(struct adv7511 *adv);
|
||||
int adv7533_attach_dsi(struct adv7511 *adv);
|
||||
void adv7533_detach_dsi(struct adv7511 *adv);
|
||||
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
|
||||
#else
|
||||
static inline void adv7533_dsi_power_on(struct adv7511 *adv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void adv7533_dsi_power_off(struct adv7511 *adv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void adv7533_mode_set(struct adv7511 *adv,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adv7533_patch_registers(struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void adv7533_uninit_cec(struct adv7511 *adv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adv7533_init_cec(struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adv7533_attach_dsi(struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void adv7533_detach_dsi(struct adv7511 *adv)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DRM_I2C_ADV7511_H__ */
|
|
@ -8,51 +8,17 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
struct adv7511 {
|
||||
struct i2c_client *i2c_main;
|
||||
struct i2c_client *i2c_edid;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regmap *packet_memory_regmap;
|
||||
enum drm_connector_status status;
|
||||
bool powered;
|
||||
|
||||
unsigned int f_tmds;
|
||||
|
||||
unsigned int current_edid_segment;
|
||||
uint8_t edid_buf[256];
|
||||
bool edid_read;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
bool embedded_sync;
|
||||
enum adv7511_sync_polarity vsync_polarity;
|
||||
enum adv7511_sync_polarity hsync_polarity;
|
||||
bool rgb;
|
||||
|
||||
struct edid *edid;
|
||||
|
||||
struct gpio_desc *gpio_pd;
|
||||
};
|
||||
|
||||
static struct adv7511 *encoder_to_adv7511(struct drm_encoder *encoder)
|
||||
{
|
||||
return to_encoder_slave(encoder)->slave_priv;
|
||||
}
|
||||
|
||||
/* ADI recommended values for proper operation. */
|
||||
static const struct reg_sequence adv7511_fixed_registers[] = {
|
||||
{ 0x98, 0x03 },
|
||||
|
@ -394,6 +360,9 @@ static void adv7511_power_on(struct adv7511 *adv7511)
|
|||
*/
|
||||
regcache_sync(adv7511->regmap);
|
||||
|
||||
if (adv7511->type == ADV7533)
|
||||
adv7533_dsi_power_on(adv7511);
|
||||
|
||||
adv7511->powered = true;
|
||||
}
|
||||
|
||||
|
@ -405,6 +374,9 @@ static void adv7511_power_off(struct adv7511 *adv7511)
|
|||
ADV7511_POWER_POWER_DOWN);
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
|
||||
if (adv7511->type == ADV7533)
|
||||
adv7533_dsi_power_off(adv7511);
|
||||
|
||||
adv7511->powered = false;
|
||||
}
|
||||
|
||||
|
@ -430,7 +402,7 @@ static bool adv7511_hpd(struct adv7511 *adv7511)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int adv7511_irq_process(struct adv7511 *adv7511)
|
||||
static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
|
||||
{
|
||||
unsigned int irq0, irq1;
|
||||
int ret;
|
||||
|
@ -446,8 +418,8 @@ static int adv7511_irq_process(struct adv7511 *adv7511)
|
|||
regmap_write(adv7511->regmap, ADV7511_REG_INT(0), irq0);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
|
||||
|
||||
if (irq0 & ADV7511_INT0_HPD && adv7511->encoder)
|
||||
drm_helper_hpd_irq_event(adv7511->encoder->dev);
|
||||
if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder)
|
||||
drm_helper_hpd_irq_event(adv7511->connector.dev);
|
||||
|
||||
if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
|
||||
adv7511->edid_read = true;
|
||||
|
@ -464,7 +436,7 @@ static irqreturn_t adv7511_irq_handler(int irq, void *devid)
|
|||
struct adv7511 *adv7511 = devid;
|
||||
int ret;
|
||||
|
||||
ret = adv7511_irq_process(adv7511);
|
||||
ret = adv7511_irq_process(adv7511, true);
|
||||
return ret < 0 ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -481,7 +453,7 @@ static int adv7511_wait_for_edid(struct adv7511 *adv7511, int timeout)
|
|||
adv7511->edid_read, msecs_to_jiffies(timeout));
|
||||
} else {
|
||||
for (; timeout > 0; timeout -= 25) {
|
||||
ret = adv7511_irq_process(adv7511);
|
||||
ret = adv7511_irq_process(adv7511, false);
|
||||
if (ret < 0)
|
||||
break;
|
||||
|
||||
|
@ -563,13 +535,12 @@ static int adv7511_get_edid_block(void *data, u8 *buf, unsigned int block,
|
|||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder operations
|
||||
* ADV75xx helpers
|
||||
*/
|
||||
|
||||
static int adv7511_get_modes(struct drm_encoder *encoder,
|
||||
static int adv7511_get_modes(struct adv7511 *adv7511,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
|
||||
struct edid *edid;
|
||||
unsigned int count;
|
||||
|
||||
|
@ -606,21 +577,9 @@ static int adv7511_get_modes(struct drm_encoder *encoder,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void adv7511_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
adv7511_power_on(adv7511);
|
||||
else
|
||||
adv7511_power_off(adv7511);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
adv7511_encoder_detect(struct drm_encoder *encoder,
|
||||
struct drm_connector *connector)
|
||||
adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector)
|
||||
{
|
||||
struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
|
||||
enum drm_connector_status status;
|
||||
unsigned int val;
|
||||
bool hpd;
|
||||
|
@ -644,7 +603,7 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
|
|||
if (status == connector_status_connected && hpd && adv7511->powered) {
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
adv7511_power_on(adv7511);
|
||||
adv7511_get_modes(encoder, connector);
|
||||
adv7511_get_modes(adv7511, connector);
|
||||
if (adv7511->status == connector_status_connected)
|
||||
status = connector_status_disconnected;
|
||||
} else {
|
||||
|
@ -658,8 +617,8 @@ adv7511_encoder_detect(struct drm_encoder *encoder,
|
|||
return status;
|
||||
}
|
||||
|
||||
static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode)
|
||||
static int adv7511_mode_valid(struct adv7511 *adv7511,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
@ -667,11 +626,10 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
|
|||
return MODE_OK;
|
||||
}
|
||||
|
||||
static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
static void adv7511_mode_set(struct adv7511 *adv7511,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
struct adv7511 *adv7511 = encoder_to_adv7511(encoder);
|
||||
unsigned int low_refresh_rate;
|
||||
unsigned int hsync_polarity = 0;
|
||||
unsigned int vsync_polarity = 0;
|
||||
|
@ -754,6 +712,11 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
|
|||
regmap_update_bits(adv7511->regmap, 0x17,
|
||||
0x60, (vsync_polarity << 6) | (hsync_polarity << 5));
|
||||
|
||||
if (adv7511->type == ADV7533)
|
||||
adv7533_mode_set(adv7511, adj_mode);
|
||||
|
||||
drm_mode_copy(&adv7511->curr_mode, adj_mode);
|
||||
|
||||
/*
|
||||
* TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is
|
||||
* supposed to give better results.
|
||||
|
@ -762,12 +725,114 @@ static void adv7511_encoder_mode_set(struct drm_encoder *encoder,
|
|||
adv7511->f_tmds = mode->clock;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_slave_funcs adv7511_encoder_funcs = {
|
||||
.dpms = adv7511_encoder_dpms,
|
||||
.mode_valid = adv7511_encoder_mode_valid,
|
||||
.mode_set = adv7511_encoder_mode_set,
|
||||
.detect = adv7511_encoder_detect,
|
||||
.get_modes = adv7511_get_modes,
|
||||
/* Connector funcs */
|
||||
static struct adv7511 *connector_to_adv7511(struct drm_connector *connector)
|
||||
{
|
||||
return container_of(connector, struct adv7511, connector);
|
||||
}
|
||||
|
||||
static int adv7511_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct adv7511 *adv = connector_to_adv7511(connector);
|
||||
|
||||
return adv7511_get_modes(adv, connector);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
adv7511_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct adv7511 *adv = connector_to_adv7511(connector);
|
||||
|
||||
return adv7511_mode_valid(adv, mode);
|
||||
}
|
||||
|
||||
static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = {
|
||||
.get_modes = adv7511_connector_get_modes,
|
||||
.mode_valid = adv7511_connector_mode_valid,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
adv7511_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct adv7511 *adv = connector_to_adv7511(connector);
|
||||
|
||||
return adv7511_detect(adv, connector);
|
||||
}
|
||||
|
||||
static struct drm_connector_funcs adv7511_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = adv7511_connector_detect,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
/* Bridge funcs */
|
||||
static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct adv7511, bridge);
|
||||
}
|
||||
|
||||
static void adv7511_bridge_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct adv7511 *adv = bridge_to_adv7511(bridge);
|
||||
|
||||
adv7511_power_on(adv);
|
||||
}
|
||||
|
||||
static void adv7511_bridge_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct adv7511 *adv = bridge_to_adv7511(bridge);
|
||||
|
||||
adv7511_power_off(adv);
|
||||
}
|
||||
|
||||
static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
{
|
||||
struct adv7511 *adv = bridge_to_adv7511(bridge);
|
||||
|
||||
adv7511_mode_set(adv, mode, adj_mode);
|
||||
}
|
||||
|
||||
static int adv7511_bridge_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct adv7511 *adv = bridge_to_adv7511(bridge);
|
||||
int ret;
|
||||
|
||||
if (!bridge->encoder) {
|
||||
DRM_ERROR("Parent encoder object not found");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
ret = drm_connector_init(bridge->dev, &adv->connector,
|
||||
&adv7511_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to initialize connector with drm\n");
|
||||
return ret;
|
||||
}
|
||||
drm_connector_helper_add(&adv->connector,
|
||||
&adv7511_connector_helper_funcs);
|
||||
drm_mode_connector_attach_encoder(&adv->connector, bridge->encoder);
|
||||
|
||||
if (adv->type == ADV7533)
|
||||
ret = adv7533_attach_dsi(adv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_bridge_funcs adv7511_bridge_funcs = {
|
||||
.enable = adv7511_bridge_enable,
|
||||
.disable = adv7511_bridge_disable,
|
||||
.mode_set = adv7511_bridge_mode_set,
|
||||
.attach = adv7511_bridge_attach,
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
|
@ -780,8 +845,6 @@ static int adv7511_parse_dt(struct device_node *np,
|
|||
const char *str;
|
||||
int ret;
|
||||
|
||||
memset(config, 0, sizeof(*config));
|
||||
|
||||
of_property_read_u32(np, "adi,input-depth", &config->input_color_depth);
|
||||
if (config->input_color_depth != 8 && config->input_color_depth != 10 &&
|
||||
config->input_color_depth != 12)
|
||||
|
@ -881,7 +944,17 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
adv7511->powered = false;
|
||||
adv7511->status = connector_status_disconnected;
|
||||
|
||||
ret = adv7511_parse_dt(dev->of_node, &link_config);
|
||||
if (dev->of_node)
|
||||
adv7511->type = (enum adv7511_type)of_device_get_match_data(dev);
|
||||
else
|
||||
adv7511->type = id->driver_data;
|
||||
|
||||
memset(&link_config, 0, sizeof(link_config));
|
||||
|
||||
if (adv7511->type == ADV7511)
|
||||
ret = adv7511_parse_dt(dev->of_node, &link_config);
|
||||
else
|
||||
ret = adv7533_parse_dt(dev->of_node, adv7511);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -907,8 +980,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
return ret;
|
||||
dev_dbg(dev, "Rev. %d\n", val);
|
||||
|
||||
ret = regmap_register_patch(adv7511->regmap, adv7511_fixed_registers,
|
||||
ARRAY_SIZE(adv7511_fixed_registers));
|
||||
if (adv7511->type == ADV7511)
|
||||
ret = regmap_register_patch(adv7511->regmap,
|
||||
adv7511_fixed_registers,
|
||||
ARRAY_SIZE(adv7511_fixed_registers));
|
||||
else
|
||||
ret = adv7533_patch_registers(adv7511);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -923,6 +1000,12 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
if (!adv7511->i2c_edid)
|
||||
return -ENOMEM;
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
ret = adv7533_init_cec(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
}
|
||||
|
||||
if (i2c->irq) {
|
||||
init_waitqueue_head(&adv7511->wq);
|
||||
|
||||
|
@ -931,7 +1014,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
IRQF_ONESHOT, dev_name(dev),
|
||||
adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_device;
|
||||
goto err_unregister_cec;
|
||||
}
|
||||
|
||||
/* CEC is unused for now */
|
||||
|
@ -942,11 +1025,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
|||
|
||||
i2c_set_clientdata(i2c, adv7511);
|
||||
|
||||
adv7511_set_link_config(adv7511, &link_config);
|
||||
if (adv7511->type == ADV7511)
|
||||
adv7511_set_link_config(adv7511, &link_config);
|
||||
|
||||
adv7511->bridge.funcs = &adv7511_bridge_funcs;
|
||||
adv7511->bridge.of_node = dev->of_node;
|
||||
|
||||
ret = drm_bridge_add(&adv7511->bridge);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to add adv7511 bridge\n");
|
||||
goto err_unregister_cec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_i2c_unregister_device:
|
||||
err_unregister_cec:
|
||||
adv7533_uninit_cec(adv7511);
|
||||
err_i2c_unregister_edid:
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
return ret;
|
||||
|
@ -956,6 +1051,13 @@ static int adv7511_remove(struct i2c_client *i2c)
|
|||
{
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
adv7533_detach_dsi(adv7511);
|
||||
adv7533_uninit_cec(adv7511);
|
||||
}
|
||||
|
||||
drm_bridge_remove(&adv7511->bridge);
|
||||
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
kfree(adv7511->edid);
|
||||
|
@ -963,59 +1065,57 @@ static int adv7511_remove(struct i2c_client *i2c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_encoder_init(struct i2c_client *i2c, struct drm_device *dev,
|
||||
struct drm_encoder_slave *encoder)
|
||||
{
|
||||
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
encoder->slave_priv = adv7511;
|
||||
encoder->slave_funcs = &adv7511_encoder_funcs;
|
||||
|
||||
adv7511->encoder = &encoder->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adv7511_i2c_ids[] = {
|
||||
{ "adv7511", 0 },
|
||||
{ "adv7511w", 0 },
|
||||
{ "adv7513", 0 },
|
||||
{ "adv7511", ADV7511 },
|
||||
{ "adv7511w", ADV7511 },
|
||||
{ "adv7513", ADV7511 },
|
||||
#ifdef CONFIG_DRM_I2C_ADV7533
|
||||
{ "adv7533", ADV7533 },
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids);
|
||||
|
||||
static const struct of_device_id adv7511_of_ids[] = {
|
||||
{ .compatible = "adi,adv7511", },
|
||||
{ .compatible = "adi,adv7511w", },
|
||||
{ .compatible = "adi,adv7513", },
|
||||
{ .compatible = "adi,adv7511", .data = (void *)ADV7511 },
|
||||
{ .compatible = "adi,adv7511w", .data = (void *)ADV7511 },
|
||||
{ .compatible = "adi,adv7513", .data = (void *)ADV7511 },
|
||||
#ifdef CONFIG_DRM_I2C_ADV7533
|
||||
{ .compatible = "adi,adv7533", .data = (void *)ADV7533 },
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adv7511_of_ids);
|
||||
|
||||
static struct drm_i2c_encoder_driver adv7511_driver = {
|
||||
.i2c_driver = {
|
||||
.driver = {
|
||||
.name = "adv7511",
|
||||
.of_match_table = adv7511_of_ids,
|
||||
},
|
||||
.id_table = adv7511_i2c_ids,
|
||||
.probe = adv7511_probe,
|
||||
.remove = adv7511_remove,
|
||||
},
|
||||
static struct mipi_dsi_driver adv7533_dsi_driver = {
|
||||
.driver.name = "adv7533",
|
||||
};
|
||||
|
||||
.encoder_init = adv7511_encoder_init,
|
||||
static struct i2c_driver adv7511_driver = {
|
||||
.driver = {
|
||||
.name = "adv7511",
|
||||
.of_match_table = adv7511_of_ids,
|
||||
},
|
||||
.id_table = adv7511_i2c_ids,
|
||||
.probe = adv7511_probe,
|
||||
.remove = adv7511_remove,
|
||||
};
|
||||
|
||||
static int __init adv7511_init(void)
|
||||
{
|
||||
return drm_i2c_encoder_register(THIS_MODULE, &adv7511_driver);
|
||||
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
|
||||
mipi_dsi_driver_register(&adv7533_dsi_driver);
|
||||
|
||||
return i2c_add_driver(&adv7511_driver);
|
||||
}
|
||||
module_init(adv7511_init);
|
||||
|
||||
static void __exit adv7511_exit(void)
|
||||
{
|
||||
drm_i2c_encoder_unregister(&adv7511_driver);
|
||||
i2c_del_driver(&adv7511_driver);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
|
||||
mipi_dsi_driver_unregister(&adv7533_dsi_driver);
|
||||
}
|
||||
module_exit(adv7511_exit);
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
static const struct reg_sequence adv7533_fixed_registers[] = {
|
||||
{ 0x16, 0x20 },
|
||||
{ 0x9a, 0xe0 },
|
||||
{ 0xba, 0x70 },
|
||||
{ 0xde, 0x82 },
|
||||
{ 0xe4, 0x40 },
|
||||
{ 0xe5, 0x80 },
|
||||
};
|
||||
|
||||
static const struct reg_sequence adv7533_cec_fixed_registers[] = {
|
||||
{ 0x15, 0xd0 },
|
||||
{ 0x17, 0xd0 },
|
||||
{ 0x24, 0x20 },
|
||||
{ 0x57, 0x11 },
|
||||
};
|
||||
|
||||
static const struct regmap_config adv7533_cec_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = 0xff,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = adv->dsi;
|
||||
struct drm_display_mode *mode = &adv->curr_mode;
|
||||
unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
|
||||
u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */
|
||||
|
||||
hsw = mode->hsync_end - mode->hsync_start;
|
||||
hfp = mode->hsync_start - mode->hdisplay;
|
||||
hbp = mode->htotal - mode->hsync_end;
|
||||
vsw = mode->vsync_end - mode->vsync_start;
|
||||
vfp = mode->vsync_start - mode->vdisplay;
|
||||
vbp = mode->vtotal - mode->vsync_end;
|
||||
|
||||
/* set pixel clock divider mode */
|
||||
regmap_write(adv->regmap_cec, 0x16,
|
||||
clock_div_by_lanes[dsi->lanes - 2] << 3);
|
||||
|
||||
/* horizontal porch params */
|
||||
regmap_write(adv->regmap_cec, 0x28, mode->htotal >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x2a, hsw >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x2b, (hsw << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x2c, hfp >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x2d, (hfp << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x2e, hbp >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x2f, (hbp << 4) & 0xff);
|
||||
|
||||
/* vertical porch params */
|
||||
regmap_write(adv->regmap_cec, 0x30, mode->vtotal >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x32, vsw >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x33, (vsw << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x34, vfp >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x35, (vfp << 4) & 0xff);
|
||||
regmap_write(adv->regmap_cec, 0x36, vbp >> 4);
|
||||
regmap_write(adv->regmap_cec, 0x37, (vbp << 4) & 0xff);
|
||||
}
|
||||
|
||||
void adv7533_dsi_power_on(struct adv7511 *adv)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = adv->dsi;
|
||||
|
||||
if (adv->use_timing_gen)
|
||||
adv7511_dsi_config_timing_gen(adv);
|
||||
|
||||
/* set number of dsi lanes */
|
||||
regmap_write(adv->regmap_cec, 0x1c, dsi->lanes << 4);
|
||||
|
||||
if (adv->use_timing_gen) {
|
||||
/* reset internal timing generator */
|
||||
regmap_write(adv->regmap_cec, 0x27, 0xcb);
|
||||
regmap_write(adv->regmap_cec, 0x27, 0x8b);
|
||||
regmap_write(adv->regmap_cec, 0x27, 0xcb);
|
||||
} else {
|
||||
/* disable internal timing generator */
|
||||
regmap_write(adv->regmap_cec, 0x27, 0x0b);
|
||||
}
|
||||
|
||||
/* enable hdmi */
|
||||
regmap_write(adv->regmap_cec, 0x03, 0x89);
|
||||
/* disable test mode */
|
||||
regmap_write(adv->regmap_cec, 0x55, 0x00);
|
||||
|
||||
regmap_register_patch(adv->regmap_cec, adv7533_cec_fixed_registers,
|
||||
ARRAY_SIZE(adv7533_cec_fixed_registers));
|
||||
}
|
||||
|
||||
void adv7533_dsi_power_off(struct adv7511 *adv)
|
||||
{
|
||||
/* disable hdmi */
|
||||
regmap_write(adv->regmap_cec, 0x03, 0x0b);
|
||||
/* disable internal timing generator */
|
||||
regmap_write(adv->regmap_cec, 0x27, 0x0b);
|
||||
}
|
||||
|
||||
void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode)
|
||||
{
|
||||
struct mipi_dsi_device *dsi = adv->dsi;
|
||||
int lanes, ret;
|
||||
|
||||
if (adv->num_dsi_lanes != 4)
|
||||
return;
|
||||
|
||||
if (mode->clock > 80000)
|
||||
lanes = 4;
|
||||
else
|
||||
lanes = 3;
|
||||
|
||||
if (lanes != dsi->lanes) {
|
||||
mipi_dsi_detach(dsi);
|
||||
dsi->lanes = lanes;
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret)
|
||||
dev_err(&dsi->dev, "failed to change host lanes\n");
|
||||
}
|
||||
}
|
||||
|
||||
int adv7533_patch_registers(struct adv7511 *adv)
|
||||
{
|
||||
return regmap_register_patch(adv->regmap,
|
||||
adv7533_fixed_registers,
|
||||
ARRAY_SIZE(adv7533_fixed_registers));
|
||||
}
|
||||
|
||||
void adv7533_uninit_cec(struct adv7511 *adv)
|
||||
{
|
||||
i2c_unregister_device(adv->i2c_cec);
|
||||
}
|
||||
|
||||
static const int cec_i2c_addr = 0x78;
|
||||
|
||||
int adv7533_init_cec(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, cec_i2c_addr >> 1);
|
||||
if (!adv->i2c_cec)
|
||||
return -ENOMEM;
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
&adv7533_cec_regmap_config);
|
||||
if (IS_ERR(adv->regmap_cec)) {
|
||||
ret = PTR_ERR(adv->regmap_cec);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_register_patch(adv->regmap_cec,
|
||||
adv7533_cec_fixed_registers,
|
||||
ARRAY_SIZE(adv7533_cec_fixed_registers));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
adv7533_uninit_cec(adv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adv7533_attach_dsi(struct adv7511 *adv)
|
||||
{
|
||||
struct device *dev = &adv->i2c_main->dev;
|
||||
struct mipi_dsi_host *host;
|
||||
struct mipi_dsi_device *dsi;
|
||||
int ret = 0;
|
||||
const struct mipi_dsi_device_info info = { .type = "adv7533",
|
||||
.channel = 0,
|
||||
.node = NULL,
|
||||
};
|
||||
|
||||
host = of_find_mipi_dsi_host_by_node(adv->host_node);
|
||||
if (!host) {
|
||||
dev_err(dev, "failed to find dsi host\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
dsi = mipi_dsi_device_register_full(host, &info);
|
||||
if (IS_ERR(dsi)) {
|
||||
dev_err(dev, "failed to create dsi device\n");
|
||||
ret = PTR_ERR(dsi);
|
||||
goto err_dsi_device;
|
||||
}
|
||||
|
||||
adv->dsi = dsi;
|
||||
|
||||
dsi->lanes = adv->num_dsi_lanes;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
|
||||
MIPI_DSI_MODE_EOT_PACKET | MIPI_DSI_MODE_VIDEO_HSE;
|
||||
|
||||
ret = mipi_dsi_attach(dsi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to attach dsi to host\n");
|
||||
goto err_dsi_attach;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_dsi_attach:
|
||||
mipi_dsi_device_unregister(dsi);
|
||||
err_dsi_device:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adv7533_detach_dsi(struct adv7511 *adv)
|
||||
{
|
||||
mipi_dsi_detach(adv->dsi);
|
||||
mipi_dsi_device_unregister(adv->dsi);
|
||||
}
|
||||
|
||||
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv)
|
||||
{
|
||||
u32 num_lanes;
|
||||
struct device_node *endpoint;
|
||||
|
||||
of_property_read_u32(np, "adi,dsi-lanes", &num_lanes);
|
||||
|
||||
if (num_lanes < 1 || num_lanes > 4)
|
||||
return -EINVAL;
|
||||
|
||||
adv->num_dsi_lanes = num_lanes;
|
||||
|
||||
endpoint = of_graph_get_next_endpoint(np, NULL);
|
||||
if (!endpoint)
|
||||
return -ENODEV;
|
||||
|
||||
adv->host_node = of_graph_get_remote_port_parent(endpoint);
|
||||
if (!adv->host_node) {
|
||||
of_node_put(endpoint);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
of_node_put(endpoint);
|
||||
of_node_put(adv->host_node);
|
||||
|
||||
adv->use_timing_gen = !of_property_read_bool(np,
|
||||
"adi,disable-timing-generator");
|
||||
|
||||
/* TODO: Check if these need to be parsed by DT or not */
|
||||
adv->rgb = true;
|
||||
adv->embedded_sync = false;
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,6 @@
|
|||
menu "I2C encoder or helper chips"
|
||||
depends on DRM && DRM_KMS_HELPER && I2C
|
||||
|
||||
config DRM_I2C_ADV7511
|
||||
tristate "AV7511 encoder"
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders.
|
||||
|
||||
config DRM_I2C_CH7006
|
||||
tristate "Chrontel ch7006 TV encoder"
|
||||
default m if DRM_NOUVEAU
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
ccflags-y := -Iinclude/drm
|
||||
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
|
||||
|
||||
ch7006-y := ch7006_drv.o ch7006_mode.o
|
||||
obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ rcar-du-drm-y := rcar_du_crtc.o \
|
|||
rcar_du_plane.o \
|
||||
rcar_du_vgacon.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \
|
||||
rcar_du_hdmienc.o
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmienc.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
|
||||
|
||||
rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_hdmicon.h"
|
||||
#include "rcar_du_hdmienc.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_lvdscon.h"
|
||||
|
@ -174,7 +173,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
|
|||
break;
|
||||
|
||||
case DRM_MODE_ENCODER_TMDS:
|
||||
ret = rcar_du_hdmi_connector_init(rcdu, renc);
|
||||
/* connector managed by the bridge driver */
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#define __RCAR_DU_ENCODER_H__
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_hdmienc;
|
||||
|
@ -30,16 +29,16 @@ enum rcar_du_encoder_type {
|
|||
};
|
||||
|
||||
struct rcar_du_encoder {
|
||||
struct drm_encoder_slave slave;
|
||||
struct drm_encoder base;
|
||||
enum rcar_du_output output;
|
||||
struct rcar_du_hdmienc *hdmi;
|
||||
struct rcar_du_lvdsenc *lvds;
|
||||
};
|
||||
|
||||
#define to_rcar_encoder(e) \
|
||||
container_of(e, struct rcar_du_encoder, slave.base)
|
||||
container_of(e, struct rcar_du_encoder, base)
|
||||
|
||||
#define rcar_encoder_to_drm_encoder(e) (&(e)->slave.base)
|
||||
#define rcar_encoder_to_drm_encoder(e) (&(e)->base)
|
||||
|
||||
struct rcar_du_connector {
|
||||
struct drm_connector connector;
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* R-Car Display Unit HDMI Connector
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_hdmicon.h"
|
||||
#include "rcar_du_kms.h"
|
||||
|
||||
#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs)
|
||||
|
||||
static int rcar_du_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct rcar_du_connector *con = to_rcar_connector(connector);
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (sfuncs->get_modes == NULL)
|
||||
return 0;
|
||||
|
||||
return sfuncs->get_modes(encoder, connector);
|
||||
}
|
||||
|
||||
static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct rcar_du_connector *con = to_rcar_connector(connector);
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (sfuncs->mode_valid == NULL)
|
||||
return MODE_OK;
|
||||
|
||||
return sfuncs->mode_valid(encoder, mode);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs connector_helper_funcs = {
|
||||
.get_modes = rcar_du_hdmi_connector_get_modes,
|
||||
.mode_valid = rcar_du_hdmi_connector_mode_valid,
|
||||
};
|
||||
|
||||
static enum drm_connector_status
|
||||
rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct rcar_du_connector *con = to_rcar_connector(connector);
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(con->encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (sfuncs->detect == NULL)
|
||||
return connector_status_unknown;
|
||||
|
||||
return sfuncs->detect(encoder, connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.detect = rcar_du_hdmi_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct rcar_du_connector *rcon;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
|
||||
rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
|
||||
if (rcon == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
connector = &rcon->connector;
|
||||
connector->display_info.width_mm = 0;
|
||||
connector->display_info.height_mm = 0;
|
||||
connector->interlace_allowed = true;
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
drm_connector_helper_add(connector, &connector_helper_funcs);
|
||||
|
||||
connector->dpms = DRM_MODE_DPMS_OFF;
|
||||
drm_object_property_set_value(&connector->base,
|
||||
rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
|
||||
|
||||
ret = drm_mode_connector_attach_encoder(connector, encoder);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rcon->encoder = renc;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* R-Car Display Unit HDMI Connector
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
*
|
||||
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __RCAR_DU_HDMICON_H__
|
||||
#define __RCAR_DU_HDMICON_H__
|
||||
|
||||
struct rcar_du_device;
|
||||
struct rcar_du_encoder;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_RCAR_HDMI)
|
||||
int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc);
|
||||
#else
|
||||
static inline int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
|
||||
struct rcar_du_encoder *renc)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __RCAR_DU_HDMICON_H__ */
|
|
@ -16,7 +16,6 @@
|
|||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
|
@ -25,20 +24,14 @@
|
|||
|
||||
struct rcar_du_hdmienc {
|
||||
struct rcar_du_encoder *renc;
|
||||
struct device *dev;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
#define to_rcar_hdmienc(e) (to_rcar_encoder(e)->hdmi)
|
||||
#define to_slave_funcs(e) (to_rcar_encoder(e)->slave.slave_funcs)
|
||||
|
||||
static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (sfuncs->dpms)
|
||||
sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF);
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
|
||||
|
@ -50,15 +43,11 @@ static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
|
|||
static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
|
||||
true);
|
||||
|
||||
if (sfuncs->dpms)
|
||||
sfuncs->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
hdmienc->enabled = true;
|
||||
}
|
||||
|
||||
|
@ -67,29 +56,21 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder,
|
|||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
|
||||
const struct drm_display_mode *mode = &crtc_state->mode;
|
||||
|
||||
if (hdmienc->renc->lvds)
|
||||
rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds,
|
||||
adjusted_mode);
|
||||
|
||||
if (sfuncs->mode_fixup == NULL)
|
||||
return 0;
|
||||
|
||||
return sfuncs->mode_fixup(encoder, mode, adjusted_mode) ? 0 : -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
|
||||
const struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
|
||||
|
||||
if (sfuncs->mode_set)
|
||||
sfuncs->mode_set(encoder, mode, adjusted_mode);
|
||||
|
||||
rcar_du_crtc_route_output(encoder->crtc, hdmienc->renc->output);
|
||||
}
|
||||
|
@ -109,7 +90,6 @@ static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder)
|
|||
rcar_du_hdmienc_disable(encoder);
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
put_device(hdmienc->dev);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs encoder_funcs = {
|
||||
|
@ -120,8 +100,7 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
|||
struct rcar_du_encoder *renc, struct device_node *np)
|
||||
{
|
||||
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
|
||||
struct drm_i2c_encoder_driver *driver;
|
||||
struct i2c_client *i2c_slave;
|
||||
struct drm_bridge *bridge;
|
||||
struct rcar_du_hdmienc *hdmienc;
|
||||
int ret;
|
||||
|
||||
|
@ -129,44 +108,29 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu,
|
|||
if (hdmienc == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Locate the slave I2C device and driver. */
|
||||
i2c_slave = of_find_i2c_device_by_node(np);
|
||||
if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) {
|
||||
dev_dbg(rcdu->dev,
|
||||
"can't get I2C slave for %s, deferring probe\n",
|
||||
of_node_full_name(np));
|
||||
/* Locate drm bridge from the hdmi encoder DT node */
|
||||
bridge = of_drm_find_bridge(np);
|
||||
if (!bridge)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
hdmienc->dev = &i2c_slave->dev;
|
||||
|
||||
if (hdmienc->dev->driver == NULL) {
|
||||
dev_dbg(rcdu->dev,
|
||||
"I2C slave %s not probed yet, deferring probe\n",
|
||||
dev_name(hdmienc->dev));
|
||||
ret = -EPROBE_DEFER;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Initialize the slave encoder. */
|
||||
driver = to_drm_i2c_encoder_driver(to_i2c_driver(hdmienc->dev->driver));
|
||||
ret = driver->encoder_init(i2c_slave, rcdu->ddev, &renc->slave);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
|
||||
DRM_MODE_ENCODER_TMDS, NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
|
||||
drm_encoder_helper_add(encoder, &encoder_helper_funcs);
|
||||
|
||||
renc->hdmi = hdmienc;
|
||||
hdmienc->renc = renc;
|
||||
|
||||
return 0;
|
||||
/* Link drm_bridge to encoder */
|
||||
bridge->encoder = encoder;
|
||||
|
||||
error:
|
||||
put_device(hdmienc->dev);
|
||||
return ret;
|
||||
ret = drm_bridge_attach(rcdu->ddev, bridge);
|
||||
if (ret) {
|
||||
drm_encoder_cleanup(encoder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue