mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-04-21 04:53:46 -04:00
Merge tag 'drm-for-v4.15' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie:
"This is the main drm pull request for v4.15.
Core:
- Atomic object lifetime fixes
- Atomic iterator improvements
- Sparse/smatch fixes
- Legacy kms ioctls to be interruptible
- EDID override improvements
- fb/gem helper cleanups
- Simple outreachy patches
- Documentation improvements
- Fix dma-buf rcu races
- DRM mode object leasing for improving VR use cases.
- vgaarb improvements for non-x86 platforms.
New driver:
- tve200: Faraday Technology TVE200 block.
This "TV Encoder" encodes a ITU-T BT.656 stream and can be found in
the StorLink SL3516 (later Cortina Systems CS3516) as well as the
Grain Media GM8180.
New bridges:
- SiI9234 support
New panels:
- S6E63J0X03, OTM8009A, Seiko 43WVF1G, 7" rpi touch panel, Toshiba
LT089AC19000, Innolux AT043TN24
i915:
- Remove Coffeelake from alpha support
- Cannonlake workarounds
- Infoframe refactoring for DisplayPort
- VBT updates
- DisplayPort vswing/emph/buffer translation refactoring
- CCS fixes
- Restore GPU clock boost on missed vblanks
- Scatter list updates for userptr allocations
- Gen9+ transition watermarks
- Display IPC (Isochronous Priority Control)
- Private PAT management
- GVT: improved error handling and pci config sanitizing
- Execlist refactoring
- Transparent Huge Page support
- User defined priorities support
- HuC/GuC firmware refactoring
- DP MST fixes
- eDP power sequencing fixes
- Use RCU instead of stop_machine
- PSR state tracking support
- Eviction fixes
- BDW DP aux channel timeout fixes
- LSPCON fixes
- Cannonlake PLL fixes
amdgpu:
- Per VM BO support
- Powerplay cleanups
- CI powerplay support
- PASID mgr for kfd
- SR-IOV fixes
- initial GPU reset for vega10
- Prime mmap support
- TTM updates
- Clock query interface for Raven
- Fence to handle ioctl
- UVD encode ring support on Polaris
- Transparent huge page DMA support
- Compute LRU pipe tweaks
- BO flag to allow buffers to opt out of implicit sync
- CTX priority setting API
- VRAM lost infrastructure plumbing
qxl:
- fix flicker since atomic rework
amdkfd:
- Further improvements from internal AMD tree
- Usermode events
- Drop radeon support
nouveau:
- Pascal temperature sensor support
- Improved BAR2 handling
- MMU rework to support Pascal MMU
exynos:
- Improved HDMI/mixer support
- HDMI audio interface support
tegra:
- Prep work for tegra186
- Cleanup/fixes
msm:
- Preemption support for a5xx
- Display fixes for 8x96 (snapdragon 820)
- Async cursor plane fixes
- FW loading rework
- GPU debugging improvements
vc4:
- Prep for DSI panels
- fix T-format tiling scanout
- New madvise ioctl
Rockchip:
- LVDS support
omapdrm:
- omap4 HDMI CEC support
etnaviv:
- GPU performance counters groundwork
sun4i:
- refactor driver load + TCON backend
- HDMI improvements
- A31 support
- Misc fixes
udl:
- Probe/EDID read fixes.
tilcdc:
- Misc fixes.
pl111:
- Support more variants
adv7511:
- Improve EDID handling.
- HDMI CEC support
sii8620:
- Add remote control support"
* tag 'drm-for-v4.15' of git://people.freedesktop.org/~airlied/linux: (1480 commits)
drm/rockchip: analogix_dp: Use mutex rather than spinlock
drm/mode_object: fix documentation for object lookups.
drm/i915: Reorder context-close to avoid calling i915_vma_close() under RCU
drm/i915: Move init_clock_gating() back to where it was
drm/i915: Prune the reservation shared fence array
drm/i915: Idle the GPU before shinking everything
drm/i915: Lock llist_del_first() vs llist_del_all()
drm/i915: Calculate ironlake intermediate watermarks correctly, v2.
drm/i915: Disable lazy PPGTT page table optimization for vGPU
drm/i915/execlists: Remove the priority "optimisation"
drm/i915: Filter out spurious execlists context-switch interrupts
drm/amdgpu: use irq-safe lock for kiq->ring_lock
drm/amdgpu: bypass lru touch for KIQ ring submission
drm/amdgpu: Potential uninitialized variable in amdgpu_vm_update_directories()
drm/amdgpu: potential uninitialized variable in amdgpu_vce_ring_parse_cs()
drm/amd/powerplay: initialize a variable before using it
drm/amd/powerplay: suppress KASAN out of bounds warning in vega10_populate_all_memory_levels
drm/amd/amdgpu: fix evicted VRAM bo adjudgement condition
drm/vblank: Tune drm_crtc_accurate_vblank_count() WARN down to a debug
drm/rockchip: add CONFIG_OF dependency for lvds
...
This commit is contained in:
@@ -71,7 +71,7 @@ config DRM_PARADE_PS8622
|
||||
|
||||
config DRM_SIL_SII8620
|
||||
tristate "Silicon Image SII8620 HDMI/MHL bridge"
|
||||
depends on OF
|
||||
depends on OF && RC_CORE
|
||||
select DRM_KMS_HELPER
|
||||
help
|
||||
Silicon Image SII8620 HDMI/MHL bridge chip driver.
|
||||
@@ -84,6 +84,14 @@ config DRM_SII902X
|
||||
---help---
|
||||
Silicon Image sii902x bridge chip driver.
|
||||
|
||||
config DRM_SII9234
|
||||
tristate "Silicon Image SII9234 HDMI/MHL bridge"
|
||||
depends on OF
|
||||
---help---
|
||||
Say Y here if you want support for the MHL interface.
|
||||
It is an I2C driver, that detects connection of MHL bridge
|
||||
and starts encapsulation of HDMI signal.
|
||||
|
||||
config DRM_TOSHIBA_TC358767
|
||||
tristate "Toshiba TC358767 eDP bridge"
|
||||
depends on OF
|
||||
|
||||
@@ -7,6 +7,7 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
|
||||
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
|
||||
obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
|
||||
obj-$(CONFIG_DRM_SII902X) += sii902x.o
|
||||
obj-$(CONFIG_DRM_SII9234) += sii9234.o
|
||||
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
|
||||
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
|
||||
|
||||
@@ -21,3 +21,11 @@ config DRM_I2C_ADV7533
|
||||
default y
|
||||
help
|
||||
Support for the Analog Devices ADV7533 DSI to HDMI encoder.
|
||||
|
||||
config DRM_I2C_ADV7511_CEC
|
||||
bool "ADV7511/33 HDMI CEC driver"
|
||||
depends on DRM_I2C_ADV7511
|
||||
select CEC_CORE
|
||||
default y
|
||||
help
|
||||
When selected the HDMI transmitter will support the CEC feature.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
adv7511-y := adv7511_drv.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7511_CEC) += adv7511_cec.o
|
||||
adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
|
||||
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
|
||||
|
||||
@@ -195,6 +195,25 @@
|
||||
#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x)
|
||||
#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, x)
|
||||
|
||||
#define ADV7511_REG_CEC_TX_FRAME_HDR 0x00
|
||||
#define ADV7511_REG_CEC_TX_FRAME_DATA0 0x01
|
||||
#define ADV7511_REG_CEC_TX_FRAME_LEN 0x10
|
||||
#define ADV7511_REG_CEC_TX_ENABLE 0x11
|
||||
#define ADV7511_REG_CEC_TX_RETRY 0x12
|
||||
#define ADV7511_REG_CEC_TX_LOW_DRV_CNT 0x14
|
||||
#define ADV7511_REG_CEC_RX_FRAME_HDR 0x15
|
||||
#define ADV7511_REG_CEC_RX_FRAME_DATA0 0x16
|
||||
#define ADV7511_REG_CEC_RX_FRAME_LEN 0x25
|
||||
#define ADV7511_REG_CEC_RX_ENABLE 0x26
|
||||
#define ADV7511_REG_CEC_RX_BUFFERS 0x4a
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_MASK 0x4b
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_0_1 0x4c
|
||||
#define ADV7511_REG_CEC_LOG_ADDR_2 0x4d
|
||||
#define ADV7511_REG_CEC_CLK_DIV 0x4e
|
||||
#define ADV7511_REG_CEC_SOFT_RESET 0x50
|
||||
|
||||
#define ADV7533_REG_CEC_OFFSET 0x70
|
||||
|
||||
enum adv7511_input_clock {
|
||||
ADV7511_INPUT_CLOCK_1X,
|
||||
ADV7511_INPUT_CLOCK_2X,
|
||||
@@ -297,6 +316,8 @@ enum adv7511_type {
|
||||
ADV7533,
|
||||
};
|
||||
|
||||
#define ADV7511_MAX_ADDRS 3
|
||||
|
||||
struct adv7511 {
|
||||
struct i2c_client *i2c_main;
|
||||
struct i2c_client *i2c_edid;
|
||||
@@ -328,8 +349,6 @@ struct adv7511 {
|
||||
enum adv7511_sync_polarity hsync_polarity;
|
||||
bool rgb;
|
||||
|
||||
struct edid *edid;
|
||||
|
||||
struct gpio_desc *gpio_pd;
|
||||
|
||||
struct regulator_bulk_data *supplies;
|
||||
@@ -343,15 +362,27 @@ struct adv7511 {
|
||||
|
||||
enum adv7511_type type;
|
||||
struct platform_device *audio_pdev;
|
||||
|
||||
struct cec_adapter *cec_adap;
|
||||
u8 cec_addr[ADV7511_MAX_ADDRS];
|
||||
u8 cec_valid_addrs;
|
||||
bool cec_enabled_adap;
|
||||
struct clk *cec_clk;
|
||||
u32 cec_clk_freq;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
|
||||
unsigned int offset);
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
|
||||
#endif
|
||||
|
||||
#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_patch_cec_registers(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);
|
||||
@@ -374,11 +405,7 @@ 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)
|
||||
static inline int adv7533_patch_cec_registers(struct adv7511 *adv)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ static const struct hdmi_codec_ops adv7511_codec_ops = {
|
||||
.get_dai_id = adv7511_hdmi_i2s_get_dai_id,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata codec_data = {
|
||||
static const struct hdmi_codec_pdata codec_data = {
|
||||
.ops = &adv7511_codec_ops,
|
||||
.max_i2s_channels = 2,
|
||||
.i2s = 1,
|
||||
|
||||
337
drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
Normal file
337
drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* adv7511_cec.c - Analog Devices ADV7511/33 cec driver
|
||||
*
|
||||
* Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* This program is free software; you may redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
#define ADV7511_INT1_CEC_MASK \
|
||||
(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
|
||||
|
||||
static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int val;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, &val))
|
||||
return;
|
||||
|
||||
if ((val & 0x01) == 0)
|
||||
return;
|
||||
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
|
||||
cec_transmit_attempt_done(adv7511->cec_adap,
|
||||
CEC_TX_STATUS_ARB_LOST);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
|
||||
u8 status;
|
||||
u8 err_cnt = 0;
|
||||
u8 nack_cnt = 0;
|
||||
u8 low_drive_cnt = 0;
|
||||
unsigned int cnt;
|
||||
|
||||
/*
|
||||
* We set this status bit since this hardware performs
|
||||
* retransmissions.
|
||||
*/
|
||||
status = CEC_TX_STATUS_MAX_RETRIES;
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
|
||||
err_cnt = 1;
|
||||
status |= CEC_TX_STATUS_ERROR;
|
||||
} else {
|
||||
nack_cnt = cnt & 0xf;
|
||||
if (nack_cnt)
|
||||
status |= CEC_TX_STATUS_NACK;
|
||||
low_drive_cnt = cnt >> 4;
|
||||
if (low_drive_cnt)
|
||||
status |= CEC_TX_STATUS_LOW_DRIVE;
|
||||
}
|
||||
cec_transmit_done(adv7511->cec_adap, status,
|
||||
0, nack_cnt, low_drive_cnt, err_cnt);
|
||||
return;
|
||||
}
|
||||
if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
|
||||
cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
|
||||
{
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
|
||||
ADV7511_INT1_CEC_TX_ARBIT_LOST |
|
||||
ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
|
||||
struct cec_msg msg = {};
|
||||
unsigned int len;
|
||||
unsigned int val;
|
||||
u8 i;
|
||||
|
||||
if (irq1 & irq_tx_mask)
|
||||
adv_cec_tx_raw_status(adv7511, irq1);
|
||||
|
||||
if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
|
||||
return;
|
||||
|
||||
if (regmap_read(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
|
||||
return;
|
||||
|
||||
msg.len = len & 0x1f;
|
||||
|
||||
if (msg.len > 16)
|
||||
msg.len = 16;
|
||||
|
||||
if (!msg.len)
|
||||
return;
|
||||
|
||||
for (i = 0; i < msg.len; i++) {
|
||||
regmap_read(adv7511->regmap_cec,
|
||||
i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
|
||||
msg.msg[i] = val;
|
||||
}
|
||||
|
||||
/* toggle to re-enable rx 1 */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
cec_received_msg(adv7511->cec_adap, &msg);
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
|
||||
if (adv7511->i2c_cec == NULL)
|
||||
return -EIO;
|
||||
|
||||
if (!adv7511->cec_enabled_adap && enable) {
|
||||
/* power up cec section */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
0x03, 0x01);
|
||||
/* legacy mode and clear all rx buffers */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
|
||||
/* initially disable tx */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
|
||||
/* enabled irqs: */
|
||||
/* tx: ready */
|
||||
/* tx: arbitration lost */
|
||||
/* tx: retry timeout */
|
||||
/* rx: ready 1 */
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1), 0x3f,
|
||||
ADV7511_INT1_CEC_MASK);
|
||||
} else if (adv7511->cec_enabled_adap && !enable) {
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
|
||||
/* disable address mask 1-3 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x70, 0x00);
|
||||
/* power down cec section */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
0x03, 0x00);
|
||||
adv7511->cec_valid_addrs = 0;
|
||||
}
|
||||
adv7511->cec_enabled_adap = enable;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
unsigned int i, free_idx = ADV7511_MAX_ADDRS;
|
||||
|
||||
if (!adv7511->cec_enabled_adap)
|
||||
return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
|
||||
|
||||
if (addr == CEC_LOG_ADDR_INVALID) {
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x70, 0);
|
||||
adv7511->cec_valid_addrs = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
|
||||
bool is_valid = adv7511->cec_valid_addrs & (1 << i);
|
||||
|
||||
if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
|
||||
free_idx = i;
|
||||
if (is_valid && adv7511->cec_addr[i] == addr)
|
||||
return 0;
|
||||
}
|
||||
if (i == ADV7511_MAX_ADDRS) {
|
||||
i = free_idx;
|
||||
if (i == ADV7511_MAX_ADDRS)
|
||||
return -ENXIO;
|
||||
}
|
||||
adv7511->cec_addr[i] = addr;
|
||||
adv7511->cec_valid_addrs |= 1 << i;
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* enable address mask 0 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x10, 0x10);
|
||||
/* set address for mask 0 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
|
||||
0x0f, addr);
|
||||
break;
|
||||
case 1:
|
||||
/* enable address mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x20, 0x20);
|
||||
/* set address for mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
|
||||
0xf0, addr << 4);
|
||||
break;
|
||||
case 2:
|
||||
/* enable address mask 2 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
|
||||
0x40, 0x40);
|
||||
/* set address for mask 1 */
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_LOG_ADDR_2 + offset,
|
||||
0x0f, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *msg)
|
||||
{
|
||||
struct adv7511 *adv7511 = cec_get_drvdata(adap);
|
||||
unsigned int offset = adv7511->type == ADV7533 ?
|
||||
ADV7533_REG_CEC_OFFSET : 0;
|
||||
u8 len = msg->len;
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* The number of retries is the number of attempts - 1, but retry
|
||||
* at least once. It's not clear if a value of 0 is allowed, so
|
||||
* let's do at least one retry.
|
||||
*/
|
||||
regmap_update_bits(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_RETRY + offset,
|
||||
0x70, max(1, attempts - 1) << 4);
|
||||
|
||||
/* blocking, clear cec tx irq status */
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
|
||||
|
||||
/* write data */
|
||||
for (i = 0; i < len; i++)
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
|
||||
msg->msg[i]);
|
||||
|
||||
/* set length (data + header) */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
|
||||
/* start transmit, enable tx */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops adv7511_cec_adap_ops = {
|
||||
.adap_enable = adv7511_cec_adap_enable,
|
||||
.adap_log_addr = adv7511_cec_adap_log_addr,
|
||||
.adap_transmit = adv7511_cec_adap_transmit,
|
||||
};
|
||||
|
||||
static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
|
||||
{
|
||||
adv7511->cec_clk = devm_clk_get(dev, "cec");
|
||||
if (IS_ERR(adv7511->cec_clk)) {
|
||||
int ret = PTR_ERR(adv7511->cec_clk);
|
||||
|
||||
adv7511->cec_clk = NULL;
|
||||
return ret;
|
||||
}
|
||||
clk_prepare_enable(adv7511->cec_clk);
|
||||
adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
|
||||
unsigned int offset)
|
||||
{
|
||||
int ret = adv7511_cec_parse_dt(dev, adv7511);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
|
||||
adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
|
||||
if (IS_ERR(adv7511->cec_adap))
|
||||
return PTR_ERR(adv7511->cec_adap);
|
||||
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
|
||||
/* cec soft reset */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
|
||||
|
||||
/* legacy mode */
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
|
||||
|
||||
regmap_write(adv7511->regmap_cec,
|
||||
ADV7511_REG_CEC_CLK_DIV + offset,
|
||||
((adv7511->cec_clk_freq / 750000) - 1) << 2);
|
||||
|
||||
ret = cec_register_adapter(adv7511->cec_adap, dev);
|
||||
if (ret) {
|
||||
cec_delete_adapter(adv7511->cec_adap);
|
||||
adv7511->cec_adap = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -11,12 +11,15 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <media/cec.h>
|
||||
|
||||
#include "adv7511.h"
|
||||
|
||||
/* ADI recommended values for proper operation. */
|
||||
@@ -199,17 +202,14 @@ static const uint16_t adv7511_csc_ycbcr_to_rgb[] = {
|
||||
|
||||
static void adv7511_set_config_csc(struct adv7511 *adv7511,
|
||||
struct drm_connector *connector,
|
||||
bool rgb)
|
||||
bool rgb, bool hdmi_mode)
|
||||
{
|
||||
struct adv7511_video_config config;
|
||||
bool output_format_422, output_format_ycbcr;
|
||||
unsigned int mode;
|
||||
uint8_t infoframe[17];
|
||||
|
||||
if (adv7511->edid)
|
||||
config.hdmi_mode = drm_detect_hdmi_monitor(adv7511->edid);
|
||||
else
|
||||
config.hdmi_mode = false;
|
||||
config.hdmi_mode = hdmi_mode;
|
||||
|
||||
hdmi_avi_infoframe_init(&config.avi_infoframe);
|
||||
|
||||
@@ -339,8 +339,10 @@ static void __adv7511_power_on(struct adv7511 *adv7511)
|
||||
*/
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
|
||||
ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR,
|
||||
ADV7511_INT1_DDC_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -376,6 +378,9 @@ static void __adv7511_power_off(struct adv7511 *adv7511)
|
||||
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
|
||||
ADV7511_POWER_POWER_DOWN,
|
||||
ADV7511_POWER_POWER_DOWN);
|
||||
regmap_update_bits(adv7511->regmap,
|
||||
ADV7511_REG_INT_ENABLE(1),
|
||||
ADV7511_INT1_DDC_ERROR, 0);
|
||||
regcache_mark_dirty(adv7511->regmap);
|
||||
}
|
||||
|
||||
@@ -426,6 +431,8 @@ static void adv7511_hpd_work(struct work_struct *work)
|
||||
|
||||
if (adv7511->connector.status != status) {
|
||||
adv7511->connector.status = status;
|
||||
if (status == connector_status_disconnected)
|
||||
cec_phys_addr_invalidate(adv7511->cec_adap);
|
||||
drm_kms_helper_hotplug_event(adv7511->connector.dev);
|
||||
}
|
||||
}
|
||||
@@ -456,6 +463,10 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
|
||||
wake_up_all(&adv7511->wq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
adv7511_cec_irq_process(adv7511, irq1);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -589,15 +600,16 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
|
||||
if (!adv7511->powered)
|
||||
__adv7511_power_off(adv7511);
|
||||
|
||||
kfree(adv7511->edid);
|
||||
adv7511->edid = edid;
|
||||
if (!edid)
|
||||
return 0;
|
||||
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
count = drm_add_edid_modes(connector, edid);
|
||||
|
||||
adv7511_set_config_csc(adv7511, connector, adv7511->rgb);
|
||||
adv7511_set_config_csc(adv7511, connector, adv7511->rgb,
|
||||
drm_detect_hdmi_monitor(edid));
|
||||
|
||||
cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
|
||||
|
||||
kfree(edid);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -833,7 +845,11 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
if (adv->i2c_main->irq)
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
else
|
||||
adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
|
||||
ret = drm_connector_init(bridge->dev, &adv->connector,
|
||||
&adv7511_connector_funcs,
|
||||
@@ -919,6 +935,65 @@ static void adv7511_uninit_regulators(struct adv7511 *adv)
|
||||
regulator_bulk_disable(adv->num_supplies, adv->supplies);
|
||||
}
|
||||
|
||||
static bool adv7511_cec_register_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533)
|
||||
reg -= ADV7533_REG_CEC_OFFSET;
|
||||
|
||||
switch (reg) {
|
||||
case ADV7511_REG_CEC_RX_FRAME_HDR:
|
||||
case ADV7511_REG_CEC_RX_FRAME_DATA0...
|
||||
ADV7511_REG_CEC_RX_FRAME_DATA0 + 14:
|
||||
case ADV7511_REG_CEC_RX_FRAME_LEN:
|
||||
case ADV7511_REG_CEC_RX_BUFFERS:
|
||||
case ADV7511_REG_CEC_TX_LOW_DRV_CNT:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config adv7511_cec_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = 0xff,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = adv7511_cec_register_volatile,
|
||||
};
|
||||
|
||||
static int adv7511_init_cec_regmap(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
|
||||
adv->i2c_main->addr - 1);
|
||||
if (!adv->i2c_cec)
|
||||
return -ENOMEM;
|
||||
i2c_set_clientdata(adv->i2c_cec, adv);
|
||||
|
||||
adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
|
||||
&adv7511_cec_regmap_config);
|
||||
if (IS_ERR(adv->regmap_cec)) {
|
||||
ret = PTR_ERR(adv->regmap_cec);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (adv->type == ADV7533) {
|
||||
ret = adv7533_patch_cec_registers(adv);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
i2c_unregister_device(adv->i2c_cec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adv7511_parse_dt(struct device_node *np,
|
||||
struct adv7511_link_config *config)
|
||||
{
|
||||
@@ -1009,6 +1084,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
struct device *dev = &i2c->dev;
|
||||
unsigned int main_i2c_addr = i2c->addr << 1;
|
||||
unsigned int edid_i2c_addr = main_i2c_addr + 4;
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
@@ -1092,11 +1168,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
goto uninit_regulators;
|
||||
}
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
ret = adv7533_init_cec(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
}
|
||||
ret = adv7511_init_cec_regmap(adv7511);
|
||||
if (ret)
|
||||
goto err_i2c_unregister_edid;
|
||||
|
||||
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
|
||||
|
||||
@@ -1111,10 +1185,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
goto err_unregister_cec;
|
||||
}
|
||||
|
||||
/* CEC is unused for now */
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
|
||||
ADV7511_CEC_CTRL_POWER_DOWN);
|
||||
|
||||
adv7511_power_off(adv7511);
|
||||
|
||||
i2c_set_clientdata(i2c, adv7511);
|
||||
@@ -1129,10 +1199,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
|
||||
|
||||
adv7511_audio_init(dev, adv7511);
|
||||
|
||||
offset = adv7511->type == ADV7533 ? ADV7533_REG_CEC_OFFSET : 0;
|
||||
|
||||
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
|
||||
ret = adv7511_cec_init(dev, adv7511, offset);
|
||||
if (ret)
|
||||
goto err_unregister_cec;
|
||||
#else
|
||||
regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
|
||||
ADV7511_CEC_CTRL_POWER_DOWN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_cec:
|
||||
adv7533_uninit_cec(adv7511);
|
||||
i2c_unregister_device(adv7511->i2c_cec);
|
||||
if (adv7511->cec_clk)
|
||||
clk_disable_unprepare(adv7511->cec_clk);
|
||||
err_i2c_unregister_edid:
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
uninit_regulators:
|
||||
@@ -1145,10 +1228,11 @@ static int adv7511_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
|
||||
|
||||
if (adv7511->type == ADV7533) {
|
||||
if (adv7511->type == ADV7533)
|
||||
adv7533_detach_dsi(adv7511);
|
||||
adv7533_uninit_cec(adv7511);
|
||||
}
|
||||
i2c_unregister_device(adv7511->i2c_cec);
|
||||
if (adv7511->cec_clk)
|
||||
clk_disable_unprepare(adv7511->cec_clk);
|
||||
|
||||
adv7511_uninit_regulators(adv7511);
|
||||
|
||||
@@ -1156,9 +1240,9 @@ static int adv7511_remove(struct i2c_client *i2c)
|
||||
|
||||
adv7511_audio_exit(adv7511);
|
||||
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
cec_unregister_adapter(adv7511->cec_adap);
|
||||
|
||||
kfree(adv7511->edid);
|
||||
i2c_unregister_device(adv7511->i2c_edid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -32,14 +32,6 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = {
|
||||
{ 0x05, 0xc8 },
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -145,37 +137,11 @@ int adv7533_patch_registers(struct adv7511 *adv)
|
||||
ARRAY_SIZE(adv7533_fixed_registers));
|
||||
}
|
||||
|
||||
void adv7533_uninit_cec(struct adv7511 *adv)
|
||||
int adv7533_patch_cec_registers(struct adv7511 *adv)
|
||||
{
|
||||
i2c_unregister_device(adv->i2c_cec);
|
||||
}
|
||||
|
||||
int adv7533_init_cec(struct adv7511 *adv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
|
||||
adv->i2c_main->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,
|
||||
return 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)
|
||||
|
||||
@@ -188,7 +188,15 @@ EXPORT_SYMBOL(drm_panel_bridge_add);
|
||||
*/
|
||||
void drm_panel_bridge_remove(struct drm_bridge *bridge)
|
||||
{
|
||||
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
struct panel_bridge *panel_bridge;
|
||||
|
||||
if (!bridge)
|
||||
return;
|
||||
|
||||
if (bridge->funcs != &panel_bridge_bridge_funcs)
|
||||
return;
|
||||
|
||||
panel_bridge = drm_bridge_to_panel_bridge(bridge);
|
||||
|
||||
drm_bridge_remove(bridge);
|
||||
devm_kfree(panel_bridge->panel->dev, bridge);
|
||||
|
||||
994
drivers/gpu/drm/bridge/sii9234.c
Normal file
994
drivers/gpu/drm/bridge/sii9234.c
Normal file
@@ -0,0 +1,994 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Samsung Electronics
|
||||
*
|
||||
* Authors:
|
||||
* Tomasz Stanislawski <t.stanislaws@samsung.com>
|
||||
* Maciej Purski <m.purski@samsung.com>
|
||||
*
|
||||
* Based on sii9234 driver created by:
|
||||
* Adam Hampson <ahampson@sta.samsung.com>
|
||||
* Erik Gilling <konkers@android.com>
|
||||
* Shankar Bandal <shankar.b@samsung.com>
|
||||
* Dharam Kumar <dharam.kr@samsung.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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program
|
||||
*
|
||||
*/
|
||||
#include <drm/bridge/mhl.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define CBUS_DEVCAP_OFFSET 0x80
|
||||
|
||||
#define SII9234_MHL_VERSION 0x11
|
||||
#define SII9234_SCRATCHPAD_SIZE 0x10
|
||||
#define SII9234_INT_STAT_SIZE 0x33
|
||||
|
||||
#define BIT_TMDS_CCTRL_TMDS_OE BIT(4)
|
||||
#define MHL_HPD_OUT_OVR_EN BIT(4)
|
||||
#define MHL_HPD_OUT_OVR_VAL BIT(5)
|
||||
#define MHL_INIT_TIMEOUT 0x0C
|
||||
|
||||
/* MHL Tx registers and bits */
|
||||
#define MHL_TX_SRST 0x05
|
||||
#define MHL_TX_SYSSTAT_REG 0x09
|
||||
#define MHL_TX_INTR1_REG 0x71
|
||||
#define MHL_TX_INTR4_REG 0x74
|
||||
#define MHL_TX_INTR1_ENABLE_REG 0x75
|
||||
#define MHL_TX_INTR4_ENABLE_REG 0x78
|
||||
#define MHL_TX_INT_CTRL_REG 0x79
|
||||
#define MHL_TX_TMDS_CCTRL 0x80
|
||||
#define MHL_TX_DISC_CTRL1_REG 0x90
|
||||
#define MHL_TX_DISC_CTRL2_REG 0x91
|
||||
#define MHL_TX_DISC_CTRL3_REG 0x92
|
||||
#define MHL_TX_DISC_CTRL4_REG 0x93
|
||||
#define MHL_TX_DISC_CTRL5_REG 0x94
|
||||
#define MHL_TX_DISC_CTRL6_REG 0x95
|
||||
#define MHL_TX_DISC_CTRL7_REG 0x96
|
||||
#define MHL_TX_DISC_CTRL8_REG 0x97
|
||||
#define MHL_TX_STAT2_REG 0x99
|
||||
#define MHL_TX_MHLTX_CTL1_REG 0xA0
|
||||
#define MHL_TX_MHLTX_CTL2_REG 0xA1
|
||||
#define MHL_TX_MHLTX_CTL4_REG 0xA3
|
||||
#define MHL_TX_MHLTX_CTL6_REG 0xA5
|
||||
#define MHL_TX_MHLTX_CTL7_REG 0xA6
|
||||
|
||||
#define RSEN_STATUS BIT(2)
|
||||
#define HPD_CHANGE_INT BIT(6)
|
||||
#define RSEN_CHANGE_INT BIT(5)
|
||||
#define RGND_READY_INT BIT(6)
|
||||
#define VBUS_LOW_INT BIT(5)
|
||||
#define CBUS_LKOUT_INT BIT(4)
|
||||
#define MHL_DISC_FAIL_INT BIT(3)
|
||||
#define MHL_EST_INT BIT(2)
|
||||
#define HPD_CHANGE_INT_MASK BIT(6)
|
||||
#define RSEN_CHANGE_INT_MASK BIT(5)
|
||||
|
||||
#define RGND_READY_MASK BIT(6)
|
||||
#define CBUS_LKOUT_MASK BIT(4)
|
||||
#define MHL_DISC_FAIL_MASK BIT(3)
|
||||
#define MHL_EST_MASK BIT(2)
|
||||
|
||||
#define SKIP_GND BIT(6)
|
||||
|
||||
#define ATT_THRESH_SHIFT 0x04
|
||||
#define ATT_THRESH_MASK (0x03 << ATT_THRESH_SHIFT)
|
||||
#define USB_D_OEN BIT(3)
|
||||
#define DEGLITCH_TIME_MASK 0x07
|
||||
#define DEGLITCH_TIME_2MS 0
|
||||
#define DEGLITCH_TIME_4MS 1
|
||||
#define DEGLITCH_TIME_8MS 2
|
||||
#define DEGLITCH_TIME_16MS 3
|
||||
#define DEGLITCH_TIME_40MS 4
|
||||
#define DEGLITCH_TIME_50MS 5
|
||||
#define DEGLITCH_TIME_60MS 6
|
||||
#define DEGLITCH_TIME_128MS 7
|
||||
|
||||
#define USB_D_OVR BIT(7)
|
||||
#define USB_ID_OVR BIT(6)
|
||||
#define DVRFLT_SEL BIT(5)
|
||||
#define BLOCK_RGND_INT BIT(4)
|
||||
#define SKIP_DEG BIT(3)
|
||||
#define CI2CA_POL BIT(2)
|
||||
#define CI2CA_WKUP BIT(1)
|
||||
#define SINGLE_ATT BIT(0)
|
||||
|
||||
#define USB_D_ODN BIT(5)
|
||||
#define VBUS_CHECK BIT(2)
|
||||
#define RGND_INTP_MASK 0x03
|
||||
#define RGND_INTP_OPEN 0
|
||||
#define RGND_INTP_2K 1
|
||||
#define RGND_INTP_1K 2
|
||||
#define RGND_INTP_SHORT 3
|
||||
|
||||
/* HDMI registers */
|
||||
#define HDMI_RX_TMDS0_CCTRL1_REG 0x10
|
||||
#define HDMI_RX_TMDS_CLK_EN_REG 0x11
|
||||
#define HDMI_RX_TMDS_CH_EN_REG 0x12
|
||||
#define HDMI_RX_PLL_CALREFSEL_REG 0x17
|
||||
#define HDMI_RX_PLL_VCOCAL_REG 0x1A
|
||||
#define HDMI_RX_EQ_DATA0_REG 0x22
|
||||
#define HDMI_RX_EQ_DATA1_REG 0x23
|
||||
#define HDMI_RX_EQ_DATA2_REG 0x24
|
||||
#define HDMI_RX_EQ_DATA3_REG 0x25
|
||||
#define HDMI_RX_EQ_DATA4_REG 0x26
|
||||
#define HDMI_RX_TMDS_ZONE_CTRL_REG 0x4C
|
||||
#define HDMI_RX_TMDS_MODE_CTRL_REG 0x4D
|
||||
|
||||
/* CBUS registers */
|
||||
#define CBUS_INT_STATUS_1_REG 0x08
|
||||
#define CBUS_INTR1_ENABLE_REG 0x09
|
||||
#define CBUS_MSC_REQ_ABORT_REASON_REG 0x0D
|
||||
#define CBUS_INT_STATUS_2_REG 0x1E
|
||||
#define CBUS_INTR2_ENABLE_REG 0x1F
|
||||
#define CBUS_LINK_CONTROL_2_REG 0x31
|
||||
#define CBUS_MHL_STATUS_REG_0 0xB0
|
||||
#define CBUS_MHL_STATUS_REG_1 0xB1
|
||||
|
||||
#define BIT_CBUS_RESET BIT(3)
|
||||
#define SET_HPD_DOWNSTREAM BIT(6)
|
||||
|
||||
/* TPI registers */
|
||||
#define TPI_DPD_REG 0x3D
|
||||
|
||||
/* Timeouts in msec */
|
||||
#define T_SRC_VBUS_CBUS_TO_STABLE 200
|
||||
#define T_SRC_CBUS_FLOAT 100
|
||||
#define T_SRC_CBUS_DEGLITCH 2
|
||||
#define T_SRC_RXSENSE_DEGLITCH 110
|
||||
|
||||
#define MHL1_MAX_CLK 75000 /* in kHz */
|
||||
|
||||
#define I2C_TPI_ADDR 0x3D
|
||||
#define I2C_HDMI_ADDR 0x49
|
||||
#define I2C_CBUS_ADDR 0x64
|
||||
|
||||
enum sii9234_state {
|
||||
ST_OFF,
|
||||
ST_D3,
|
||||
ST_RGND_INIT,
|
||||
ST_RGND_1K,
|
||||
ST_RSEN_HIGH,
|
||||
ST_MHL_ESTABLISHED,
|
||||
ST_FAILURE_DISCOVERY,
|
||||
ST_FAILURE,
|
||||
};
|
||||
|
||||
struct sii9234 {
|
||||
struct i2c_client *client[4];
|
||||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio_reset;
|
||||
int i2c_error;
|
||||
struct regulator_bulk_data supplies[4];
|
||||
|
||||
struct mutex lock; /* Protects fields below and device registers */
|
||||
enum sii9234_state state;
|
||||
};
|
||||
|
||||
enum sii9234_client_id {
|
||||
I2C_MHL,
|
||||
I2C_TPI,
|
||||
I2C_HDMI,
|
||||
I2C_CBUS,
|
||||
};
|
||||
|
||||
static const char * const sii9234_client_name[] = {
|
||||
[I2C_MHL] = "MHL",
|
||||
[I2C_TPI] = "TPI",
|
||||
[I2C_HDMI] = "HDMI",
|
||||
[I2C_CBUS] = "CBUS",
|
||||
};
|
||||
|
||||
static int sii9234_writeb(struct sii9234 *ctx, int id, int offset,
|
||||
int value)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, offset, value);
|
||||
if (ret < 0)
|
||||
dev_err(ctx->dev, "writeb: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_writebm(struct sii9234 *ctx, int id, int offset,
|
||||
int value, int mask)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
value = (value & mask) | (ret & ~mask);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, offset, value);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
|
||||
sii9234_client_name[id], offset, value);
|
||||
ctx->i2c_error = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_readb(struct sii9234 *ctx, int id, int offset)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = ctx->client[id];
|
||||
|
||||
if (ctx->i2c_error)
|
||||
return ctx->i2c_error;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, offset);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
|
||||
sii9234_client_name[id], offset);
|
||||
ctx->i2c_error = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
|
||||
sii9234_client_name[id], offset);
|
||||
ctx->i2c_error = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sii9234_clear_error(struct sii9234 *ctx)
|
||||
{
|
||||
int ret = ctx->i2c_error;
|
||||
|
||||
ctx->i2c_error = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define mhl_tx_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_MHL, offset, value)
|
||||
#define mhl_tx_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_MHL, offset, value, mask)
|
||||
#define mhl_tx_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_MHL, offset)
|
||||
#define cbus_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_CBUS, offset, value)
|
||||
#define cbus_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_CBUS, offset, value, mask)
|
||||
#define cbus_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_CBUS, offset)
|
||||
#define hdmi_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_HDMI, offset, value)
|
||||
#define hdmi_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_HDMI, offset, value, mask)
|
||||
#define hdmi_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_HDMI, offset)
|
||||
#define tpi_writeb(sii9234, offset, value) \
|
||||
sii9234_writeb(sii9234, I2C_TPI, offset, value)
|
||||
#define tpi_writebm(sii9234, offset, value, mask) \
|
||||
sii9234_writebm(sii9234, I2C_TPI, offset, value, mask)
|
||||
#define tpi_readb(sii9234, offset) \
|
||||
sii9234_readb(sii9234, I2C_TPI, offset)
|
||||
|
||||
static u8 sii9234_tmds_control(struct sii9234 *ctx, bool enable)
|
||||
{
|
||||
mhl_tx_writebm(ctx, MHL_TX_TMDS_CCTRL, enable ? ~0 : 0,
|
||||
BIT_TMDS_CCTRL_TMDS_OE);
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, enable ? ~0 : 0,
|
||||
MHL_HPD_OUT_OVR_EN | MHL_HPD_OUT_OVR_VAL);
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_cbus_reset(struct sii9234 *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
mhl_tx_writebm(ctx, MHL_TX_SRST, ~0, BIT_CBUS_RESET);
|
||||
msleep(T_SRC_CBUS_DEGLITCH);
|
||||
mhl_tx_writebm(ctx, MHL_TX_SRST, 0, BIT_CBUS_RESET);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/*
|
||||
* Enable WRITE_STAT interrupt for writes to all
|
||||
* 4 MSC Status registers.
|
||||
*/
|
||||
cbus_writeb(ctx, 0xE0 + i, 0xF2);
|
||||
/*
|
||||
* Enable SET_INT interrupt for writes to all
|
||||
* 4 MSC Interrupt registers.
|
||||
*/
|
||||
cbus_writeb(ctx, 0xF0 + i, 0xF2);
|
||||
}
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
/* Require to chek mhl imformation of samsung in cbus_init_register */
|
||||
static int sii9234_cbus_init(struct sii9234 *ctx)
|
||||
{
|
||||
cbus_writeb(ctx, 0x07, 0xF2);
|
||||
cbus_writeb(ctx, 0x40, 0x03);
|
||||
cbus_writeb(ctx, 0x42, 0x06);
|
||||
cbus_writeb(ctx, 0x36, 0x0C);
|
||||
cbus_writeb(ctx, 0x3D, 0xFD);
|
||||
cbus_writeb(ctx, 0x1C, 0x01);
|
||||
cbus_writeb(ctx, 0x1D, 0x0F);
|
||||
cbus_writeb(ctx, 0x44, 0x02);
|
||||
/* Setup our devcap */
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEV_STATE, 0x00);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_MHL_VERSION,
|
||||
SII9234_MHL_VERSION);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_CAT,
|
||||
MHL_DCAP_CAT_SOURCE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_H, 0x01);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_L, 0x41);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VID_LINK_MODE,
|
||||
MHL_DCAP_VID_LINK_RGB444 | MHL_DCAP_VID_LINK_YCBCR444);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VIDEO_TYPE,
|
||||
MHL_DCAP_VT_GRAPHICS);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_LOG_DEV_MAP,
|
||||
MHL_DCAP_LD_GUI);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_BANDWIDTH, 0x0F);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_FEATURE_FLAG,
|
||||
MHL_DCAP_FEATURE_RCP_SUPPORT | MHL_DCAP_FEATURE_RAP_SUPPORT
|
||||
| MHL_DCAP_FEATURE_SP_SUPPORT);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_H, 0x0);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_L, 0x0);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_SCRATCHPAD_SIZE,
|
||||
SII9234_SCRATCHPAD_SIZE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_INT_STAT_SIZE,
|
||||
SII9234_INT_STAT_SIZE);
|
||||
cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_RESERVED, 0);
|
||||
cbus_writebm(ctx, 0x31, 0x0C, 0x0C);
|
||||
cbus_writeb(ctx, 0x30, 0x01);
|
||||
cbus_writebm(ctx, 0x3C, 0x30, 0x38);
|
||||
cbus_writebm(ctx, 0x22, 0x0D, 0x0F);
|
||||
cbus_writebm(ctx, 0x2E, 0x15, 0x15);
|
||||
cbus_writeb(ctx, CBUS_INTR1_ENABLE_REG, 0);
|
||||
cbus_writeb(ctx, CBUS_INTR2_ENABLE_REG, 0);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static void force_usb_id_switch_open(struct sii9234 *ctx)
|
||||
{
|
||||
/* Disable CBUS discovery */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0, 0x01);
|
||||
/* Force USB ID switch to open */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
|
||||
/* Force upstream HPD to 0 when not in MHL mode. */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x30);
|
||||
}
|
||||
|
||||
static void release_usb_id_switch_open(struct sii9234 *ctx)
|
||||
{
|
||||
msleep(T_SRC_CBUS_FLOAT);
|
||||
/* Clear USB ID switch to open */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR);
|
||||
/* Enable CBUS discovery */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 0x01);
|
||||
}
|
||||
|
||||
static int sii9234_power_init(struct sii9234 *ctx)
|
||||
{
|
||||
/* Force the SiI9234 into the D0 state. */
|
||||
tpi_writeb(ctx, TPI_DPD_REG, 0x3F);
|
||||
/* Enable TxPLL Clock */
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_CLK_EN_REG, 0x01);
|
||||
/* Enable Tx Clock Path & Equalizer */
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_CH_EN_REG, 0x15);
|
||||
/* Power Up TMDS */
|
||||
mhl_tx_writeb(ctx, 0x08, 0x35);
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_hdmi_init(struct sii9234 *ctx)
|
||||
{
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
|
||||
hdmi_writeb(ctx, HDMI_RX_PLL_CALREFSEL_REG, 0x03);
|
||||
hdmi_writeb(ctx, HDMI_RX_PLL_VCOCAL_REG, 0x20);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA0_REG, 0x8A);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA1_REG, 0x6A);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA2_REG, 0xAA);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA3_REG, 0xCA);
|
||||
hdmi_writeb(ctx, HDMI_RX_EQ_DATA4_REG, 0xEA);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_ZONE_CTRL_REG, 0xA0);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS_MODE_CTRL_REG, 0x00);
|
||||
mhl_tx_writeb(ctx, MHL_TX_TMDS_CCTRL, 0x34);
|
||||
hdmi_writeb(ctx, 0x45, 0x44);
|
||||
hdmi_writeb(ctx, 0x31, 0x0A);
|
||||
hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_mhl_tx_ctl_int(struct sii9234 *ctx)
|
||||
{
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0xD0);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL2_REG, 0xFC);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL4_REG, 0xEB);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL7_REG, 0x0C);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_reset(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
ret = sii9234_power_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_cbus_reset(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_hdmi_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_mhl_tx_ctl_int(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable HDCP Compliance safety */
|
||||
mhl_tx_writeb(ctx, 0x2B, 0x01);
|
||||
/* CBUS discovery cycle time for each drive and float = 150us */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0x04, 0x06);
|
||||
/* Clear bit 6 (reg_skip_rgnd) */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL2_REG, (1 << 7) /* Reserved */
|
||||
| 2 << ATT_THRESH_SHIFT | DEGLITCH_TIME_50MS);
|
||||
/*
|
||||
* Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel
|
||||
* 1.8V CBUS VTH & GND threshold
|
||||
* to meet CTS 3.3.7.2 spec
|
||||
*/
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
|
||||
cbus_writebm(ctx, CBUS_LINK_CONTROL_2_REG, ~0, MHL_INIT_TIMEOUT);
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL6_REG, 0xA0);
|
||||
/* RGND & single discovery attempt (RGND blocking) */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT |
|
||||
DVRFLT_SEL | SINGLE_ATT);
|
||||
/* Use VBUS path of discovery state machine */
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL8_REG, 0);
|
||||
/* 0x92[3] sets the CBUS / ID switch */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
|
||||
/*
|
||||
* To allow RGND engine to operate correctly.
|
||||
* When moving the chip from D2 to D0 (power up, init regs)
|
||||
* the values should be
|
||||
* 94[1:0] = 01 reg_cbusmhl_pup_sel[1:0] should be set for 5k
|
||||
* 93[7:6] = 10 reg_cbusdisc_pup_sel[1:0] should be
|
||||
* set for 10k (default)
|
||||
* 93[5:4] = 00 reg_cbusidle_pup_sel[1:0] = open (default)
|
||||
*/
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
|
||||
/*
|
||||
* Change from CC to 8C to match 5K
|
||||
* to meet CTS 3.3.72 spec
|
||||
*/
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
|
||||
/* Configure the interrupt as active high */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x06);
|
||||
|
||||
msleep(25);
|
||||
|
||||
/* Release usb_id switch */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR);
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL1_REG, 0x27);
|
||||
|
||||
ret = sii9234_clear_error(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sii9234_cbus_init(ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable Auto soft reset on SCDT = 0 */
|
||||
mhl_tx_writeb(ctx, 0x05, 0x04);
|
||||
/* HDMI Transcode mode enable */
|
||||
mhl_tx_writeb(ctx, 0x0D, 0x1C);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR4_ENABLE_REG,
|
||||
RGND_READY_MASK | CBUS_LKOUT_MASK
|
||||
| MHL_DISC_FAIL_MASK | MHL_EST_MASK);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG, 0x60);
|
||||
|
||||
/* This point is very important before measure RGND impedance */
|
||||
force_usb_id_switch_open(ctx);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, 0, 0xF0);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL5_REG, 0, 0x03);
|
||||
release_usb_id_switch_open(ctx);
|
||||
|
||||
/* Force upstream HPD to 0 when not in MHL mode */
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 1 << 5);
|
||||
mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, ~0, 1 << 4);
|
||||
|
||||
return sii9234_clear_error(ctx);
|
||||
}
|
||||
|
||||
static int sii9234_goto_d3(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctx->dev, "sii9234: detection started d3\n");
|
||||
|
||||
ret = sii9234_reset(ctx);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
hdmi_writeb(ctx, 0x01, 0x03);
|
||||
tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
|
||||
/* I2C above is expected to fail because power goes down */
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
ctx->state = ST_D3;
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
dev_err(ctx->dev, "%s failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sii9234_hw_on(struct sii9234 *ctx)
|
||||
{
|
||||
return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static void sii9234_hw_off(struct sii9234 *ctx)
|
||||
{
|
||||
gpiod_set_value(ctx->gpio_reset, 1);
|
||||
msleep(20);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
|
||||
}
|
||||
|
||||
static void sii9234_hw_reset(struct sii9234 *ctx)
|
||||
{
|
||||
gpiod_set_value(ctx->gpio_reset, 1);
|
||||
msleep(20);
|
||||
gpiod_set_value(ctx->gpio_reset, 0);
|
||||
}
|
||||
|
||||
static void sii9234_cable_in(struct sii9234 *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
if (ctx->state != ST_OFF)
|
||||
goto unlock;
|
||||
ret = sii9234_hw_on(ctx);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
sii9234_hw_reset(ctx);
|
||||
sii9234_goto_d3(ctx);
|
||||
/* To avoid irq storm, when hw is in meta state */
|
||||
enable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static void sii9234_cable_out(struct sii9234 *ctx)
|
||||
{
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
if (ctx->state == ST_OFF)
|
||||
goto unlock;
|
||||
|
||||
disable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
tpi_writeb(ctx, TPI_DPD_REG, 0);
|
||||
/* Turn on&off hpd festure for only QCT HDMI */
|
||||
sii9234_hw_off(ctx);
|
||||
|
||||
ctx->state = ST_OFF;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_rgnd_ready_irq(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
if (ctx->state == ST_D3) {
|
||||
int ret;
|
||||
|
||||
dev_dbg(ctx->dev, "RGND_READY_INT\n");
|
||||
sii9234_hw_reset(ctx);
|
||||
|
||||
ret = sii9234_reset(ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(ctx->dev, "sii9234_reset() failed\n");
|
||||
return ST_FAILURE;
|
||||
}
|
||||
|
||||
return ST_RGND_INIT;
|
||||
}
|
||||
|
||||
/* Got interrupt in inappropriate state */
|
||||
if (ctx->state != ST_RGND_INIT)
|
||||
return ST_FAILURE;
|
||||
|
||||
value = mhl_tx_readb(ctx, MHL_TX_STAT2_REG);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
if ((value & RGND_INTP_MASK) != RGND_INTP_1K) {
|
||||
dev_warn(ctx->dev, "RGND is not 1k\n");
|
||||
return ST_RGND_INIT;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RGND 1K!!\n");
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
|
||||
mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, 0x05);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
msleep(T_SRC_VBUS_CBUS_TO_STABLE);
|
||||
return ST_RGND_1K;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_mhl_established(struct sii9234 *ctx)
|
||||
{
|
||||
dev_dbg(ctx->dev, "mhl est interrupt\n");
|
||||
|
||||
/* Discovery override */
|
||||
mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0x10);
|
||||
/* Increase DDC translation layer timer (byte mode) */
|
||||
cbus_writeb(ctx, 0x07, 0x32);
|
||||
cbus_writebm(ctx, 0x44, ~0, 1 << 1);
|
||||
/* Keep the discovery enabled. Need RGND interrupt */
|
||||
mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 1);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG,
|
||||
RSEN_CHANGE_INT_MASK | HPD_CHANGE_INT_MASK);
|
||||
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
return ST_MHL_ESTABLISHED;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_hpd_change(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
value = cbus_readb(ctx, CBUS_MSC_REQ_ABORT_REASON_REG);
|
||||
if (sii9234_clear_error(ctx))
|
||||
return ST_FAILURE;
|
||||
|
||||
if (value & SET_HPD_DOWNSTREAM) {
|
||||
/* Downstream HPD High, Enable TMDS */
|
||||
sii9234_tmds_control(ctx, true);
|
||||
} else {
|
||||
/* Downstream HPD Low, Disable TMDS */
|
||||
sii9234_tmds_control(ctx, false);
|
||||
}
|
||||
|
||||
return ctx->state;
|
||||
}
|
||||
|
||||
static enum sii9234_state sii9234_rsen_change(struct sii9234 *ctx)
|
||||
{
|
||||
int value;
|
||||
|
||||
/* Work_around code to handle wrong interrupt */
|
||||
if (ctx->state != ST_RGND_1K) {
|
||||
dev_err(ctx->dev, "RSEN_HIGH without RGND_1K\n");
|
||||
return ST_FAILURE;
|
||||
}
|
||||
value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
|
||||
if (value < 0)
|
||||
return ST_FAILURE;
|
||||
|
||||
if (value & RSEN_STATUS) {
|
||||
dev_dbg(ctx->dev, "MHL cable connected.. RSEN High\n");
|
||||
return ST_RSEN_HIGH;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RSEN lost\n");
|
||||
/*
|
||||
* Once RSEN loss is confirmed,we need to check
|
||||
* based on cable status and chip power status,whether
|
||||
* it is SINK Loss(HDMI cable not connected, TV Off)
|
||||
* or MHL cable disconnection
|
||||
* TODO: Define the below mhl_disconnection()
|
||||
*/
|
||||
msleep(T_SRC_RXSENSE_DEGLITCH);
|
||||
value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
|
||||
if (value < 0)
|
||||
return ST_FAILURE;
|
||||
dev_dbg(ctx->dev, "sys_stat: %x\n", value);
|
||||
|
||||
if (value & RSEN_STATUS) {
|
||||
dev_dbg(ctx->dev, "RSEN recovery\n");
|
||||
return ST_RSEN_HIGH;
|
||||
}
|
||||
dev_dbg(ctx->dev, "RSEN Really LOW\n");
|
||||
/* To meet CTS 3.3.22.2 spec */
|
||||
sii9234_tmds_control(ctx, false);
|
||||
force_usb_id_switch_open(ctx);
|
||||
release_usb_id_switch_open(ctx);
|
||||
|
||||
return ST_FAILURE;
|
||||
}
|
||||
|
||||
static irqreturn_t sii9234_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct sii9234 *ctx = data;
|
||||
int intr1, intr4;
|
||||
int intr1_en, intr4_en;
|
||||
int cbus_intr1, cbus_intr2;
|
||||
|
||||
dev_dbg(ctx->dev, "%s\n", __func__);
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
intr1 = mhl_tx_readb(ctx, MHL_TX_INTR1_REG);
|
||||
intr4 = mhl_tx_readb(ctx, MHL_TX_INTR4_REG);
|
||||
intr1_en = mhl_tx_readb(ctx, MHL_TX_INTR1_ENABLE_REG);
|
||||
intr4_en = mhl_tx_readb(ctx, MHL_TX_INTR4_ENABLE_REG);
|
||||
cbus_intr1 = cbus_readb(ctx, CBUS_INT_STATUS_1_REG);
|
||||
cbus_intr2 = cbus_readb(ctx, CBUS_INT_STATUS_2_REG);
|
||||
|
||||
if (sii9234_clear_error(ctx))
|
||||
goto done;
|
||||
|
||||
dev_dbg(ctx->dev, "irq %02x/%02x %02x/%02x %02x/%02x\n",
|
||||
intr1, intr1_en, intr4, intr4_en, cbus_intr1, cbus_intr2);
|
||||
|
||||
if (intr4 & RGND_READY_INT)
|
||||
ctx->state = sii9234_rgnd_ready_irq(ctx);
|
||||
if (intr1 & RSEN_CHANGE_INT)
|
||||
ctx->state = sii9234_rsen_change(ctx);
|
||||
if (intr4 & MHL_EST_INT)
|
||||
ctx->state = sii9234_mhl_established(ctx);
|
||||
if (intr1 & HPD_CHANGE_INT)
|
||||
ctx->state = sii9234_hpd_change(ctx);
|
||||
if (intr4 & CBUS_LKOUT_INT)
|
||||
ctx->state = ST_FAILURE;
|
||||
if (intr4 & MHL_DISC_FAIL_INT)
|
||||
ctx->state = ST_FAILURE_DISCOVERY;
|
||||
|
||||
done:
|
||||
/* Clean interrupt status and pending flags */
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR1_REG, intr1);
|
||||
mhl_tx_writeb(ctx, MHL_TX_INTR4_REG, intr4);
|
||||
cbus_writeb(ctx, CBUS_MHL_STATUS_REG_0, 0xFF);
|
||||
cbus_writeb(ctx, CBUS_MHL_STATUS_REG_1, 0xFF);
|
||||
cbus_writeb(ctx, CBUS_INT_STATUS_1_REG, cbus_intr1);
|
||||
cbus_writeb(ctx, CBUS_INT_STATUS_2_REG, cbus_intr2);
|
||||
|
||||
sii9234_clear_error(ctx);
|
||||
|
||||
if (ctx->state == ST_FAILURE) {
|
||||
dev_dbg(ctx->dev, "try to reset after failure\n");
|
||||
sii9234_hw_reset(ctx);
|
||||
sii9234_goto_d3(ctx);
|
||||
}
|
||||
|
||||
if (ctx->state == ST_FAILURE_DISCOVERY) {
|
||||
dev_err(ctx->dev, "discovery failed, no power for MHL?\n");
|
||||
tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
|
||||
ctx->state = ST_D3;
|
||||
}
|
||||
|
||||
mutex_unlock(&ctx->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sii9234_init_resources(struct sii9234 *ctx,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int ret;
|
||||
|
||||
if (!ctx->dev->of_node) {
|
||||
dev_err(ctx->dev, "not DT device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx->gpio_reset = devm_gpiod_get(ctx->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpio_reset)) {
|
||||
dev_err(ctx->dev, "failed to get reset gpio from DT\n");
|
||||
return PTR_ERR(ctx->gpio_reset);
|
||||
}
|
||||
|
||||
ctx->supplies[0].supply = "avcc12";
|
||||
ctx->supplies[1].supply = "avcc33";
|
||||
ctx->supplies[2].supply = "iovcc18";
|
||||
ctx->supplies[3].supply = "cvcc12";
|
||||
ret = devm_regulator_bulk_get(ctx->dev, 4, ctx->supplies);
|
||||
if (ret) {
|
||||
dev_err(ctx->dev, "regulator_bulk failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->client[I2C_MHL] = client;
|
||||
|
||||
ctx->client[I2C_TPI] = i2c_new_dummy(adapter, I2C_TPI_ADDR);
|
||||
if (!ctx->client[I2C_TPI]) {
|
||||
dev_err(ctx->dev, "failed to create TPI client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx->client[I2C_HDMI] = i2c_new_dummy(adapter, I2C_HDMI_ADDR);
|
||||
if (!ctx->client[I2C_HDMI]) {
|
||||
dev_err(ctx->dev, "failed to create HDMI RX client\n");
|
||||
goto fail_tpi;
|
||||
}
|
||||
|
||||
ctx->client[I2C_CBUS] = i2c_new_dummy(adapter, I2C_CBUS_ADDR);
|
||||
if (!ctx->client[I2C_CBUS]) {
|
||||
dev_err(ctx->dev, "failed to create CBUS client\n");
|
||||
goto fail_hdmi;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_hdmi:
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
fail_tpi:
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void sii9234_deinit_resources(struct sii9234 *ctx)
|
||||
{
|
||||
i2c_unregister_device(ctx->client[I2C_CBUS]);
|
||||
i2c_unregister_device(ctx->client[I2C_HDMI]);
|
||||
i2c_unregister_device(ctx->client[I2C_TPI]);
|
||||
}
|
||||
|
||||
static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct sii9234, bridge);
|
||||
}
|
||||
|
||||
static enum drm_mode_status sii9234_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > MHL1_MAX_CLK)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs sii9234_bridge_funcs = {
|
||||
.mode_valid = sii9234_mode_valid,
|
||||
};
|
||||
|
||||
static int sii9234_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
struct sii9234 *ctx;
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->dev = dev;
|
||||
mutex_init(&ctx->lock);
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(dev, "I2C adapter lacks SMBUS feature\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "no irq provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
sii9234_irq_thread,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"sii9234", ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to install IRQ handler\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sii9234_init_resources(ctx, client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
i2c_set_clientdata(client, ctx);
|
||||
|
||||
ctx->bridge.funcs = &sii9234_bridge_funcs;
|
||||
ctx->bridge.of_node = dev->of_node;
|
||||
drm_bridge_add(&ctx->bridge);
|
||||
|
||||
sii9234_cable_in(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sii9234_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sii9234 *ctx = i2c_get_clientdata(client);
|
||||
|
||||
sii9234_cable_out(ctx);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
sii9234_deinit_resources(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sii9234_dt_match[] = {
|
||||
{ .compatible = "sil,sii9234" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sii9234_dt_match);
|
||||
|
||||
static const struct i2c_device_id sii9234_id[] = {
|
||||
{ "SII9234", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sii9234_id);
|
||||
|
||||
static struct i2c_driver sii9234_driver = {
|
||||
.driver = {
|
||||
.name = "sii9234",
|
||||
.of_match_table = sii9234_dt_match,
|
||||
},
|
||||
.probe = sii9234_probe,
|
||||
.remove = sii9234_remove,
|
||||
.id_table = sii9234_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(sii9234_driver);
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <media/rc-core.h>
|
||||
|
||||
#include "sil-sii8620.h"
|
||||
|
||||
#define SII8620_BURST_BUF_LEN 288
|
||||
@@ -58,6 +60,7 @@ enum sii8620_mt_state {
|
||||
struct sii8620 {
|
||||
struct drm_bridge bridge;
|
||||
struct device *dev;
|
||||
struct rc_dev *rc_dev;
|
||||
struct clk *clk_xtal;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_int;
|
||||
@@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code)
|
||||
{
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code)
|
||||
{
|
||||
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code);
|
||||
}
|
||||
|
||||
static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
|
||||
struct sii8620_mt_msg *msg)
|
||||
{
|
||||
@@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx)
|
||||
sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf));
|
||||
}
|
||||
|
||||
static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
|
||||
{
|
||||
bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK);
|
||||
|
||||
scancode &= MHL_RCP_KEY_ID_MASK;
|
||||
|
||||
if (!ctx->rc_dev) {
|
||||
dev_dbg(ctx->dev, "RCP input device not initialized\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pressed)
|
||||
rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
|
||||
else
|
||||
rc_keyup(ctx->rc_dev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
|
||||
{
|
||||
u8 ints[MHL_INT_SIZE];
|
||||
@@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx)
|
||||
|
||||
static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
|
||||
{
|
||||
struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
|
||||
struct sii8620_mt_msg *msg;
|
||||
u8 buf[2];
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
|
||||
|
||||
switch (buf[0]) {
|
||||
case MHL_MSC_MSG_RAPK:
|
||||
msg = sii8620_msc_msg_first(ctx);
|
||||
if (!msg)
|
||||
return;
|
||||
msg->ret = buf[1];
|
||||
ctx->mt_state = MT_STATE_DONE;
|
||||
break;
|
||||
case MHL_MSC_MSG_RCP:
|
||||
if (!sii8620_rcp_consume(ctx, buf[1]))
|
||||
sii8620_mt_rcpe(ctx,
|
||||
MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
|
||||
sii8620_mt_rcpk(ctx, buf[1]);
|
||||
break;
|
||||
default:
|
||||
dev_err(ctx->dev, "%s message type %d,%d not supported",
|
||||
__func__, buf[0], buf[1]);
|
||||
@@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx)
|
||||
enable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
}
|
||||
|
||||
static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
|
||||
{
|
||||
struct rc_dev *rc_dev;
|
||||
int ret;
|
||||
|
||||
rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
|
||||
if (!rc_dev) {
|
||||
dev_err(ctx->dev, "Failed to allocate RC device\n");
|
||||
ctx->error = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
rc_dev->input_phys = "sii8620/input0";
|
||||
rc_dev->input_id.bustype = BUS_VIRTUAL;
|
||||
rc_dev->map_name = RC_MAP_CEC;
|
||||
rc_dev->allowed_protocols = RC_PROTO_BIT_CEC;
|
||||
rc_dev->driver_name = "sii8620";
|
||||
rc_dev->device_name = "sii8620";
|
||||
|
||||
ret = rc_register_device(rc_dev);
|
||||
|
||||
if (ret) {
|
||||
dev_err(ctx->dev, "Failed to register RC device\n");
|
||||
ctx->error = ret;
|
||||
rc_free_device(ctx->rc_dev);
|
||||
return;
|
||||
}
|
||||
ctx->rc_dev = rc_dev;
|
||||
}
|
||||
|
||||
static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
|
||||
{
|
||||
return container_of(bridge, struct sii8620, bridge);
|
||||
}
|
||||
|
||||
static int sii8620_attach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
||||
|
||||
sii8620_init_rcp_input_dev(ctx);
|
||||
|
||||
return sii8620_clear_error(ctx);
|
||||
}
|
||||
|
||||
static void sii8620_detach(struct drm_bridge *bridge)
|
||||
{
|
||||
struct sii8620 *ctx = bridge_to_sii8620(bridge);
|
||||
|
||||
rc_unregister_device(ctx->rc_dev);
|
||||
}
|
||||
|
||||
static bool sii8620_mode_fixup(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
@@ -2151,6 +2235,8 @@ end:
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs sii8620_bridge_funcs = {
|
||||
.attach = sii8620_attach,
|
||||
.detach = sii8620_detach,
|
||||
.mode_fixup = sii8620_mode_fixup,
|
||||
};
|
||||
|
||||
@@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client)
|
||||
struct sii8620 *ctx = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq(to_i2c_client(ctx->dev)->irq);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
sii8620_hw_off(ctx);
|
||||
drm_bridge_remove(&ctx->bridge);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,19 +30,20 @@
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#define DSI_VERSION 0x00
|
||||
|
||||
#define DSI_PWR_UP 0x04
|
||||
#define RESET 0
|
||||
#define POWERUP BIT(0)
|
||||
|
||||
#define DSI_CLKMGR_CFG 0x08
|
||||
#define TO_CLK_DIVIDSION(div) (((div) & 0xff) << 8)
|
||||
#define TX_ESC_CLK_DIVIDSION(div) (((div) & 0xff) << 0)
|
||||
#define TO_CLK_DIVISION(div) (((div) & 0xff) << 8)
|
||||
#define TX_ESC_CLK_DIVISION(div) ((div) & 0xff)
|
||||
|
||||
#define DSI_DPI_VCID 0x0c
|
||||
#define DPI_VID(vid) (((vid) & 0x3) << 0)
|
||||
#define DPI_VCID(vcid) ((vcid) & 0x3)
|
||||
|
||||
#define DSI_DPI_COLOR_CODING 0x10
|
||||
#define EN18_LOOSELY BIT(8)
|
||||
#define LOOSELY18_EN BIT(8)
|
||||
#define DPI_COLOR_CODING_16BIT_1 0x0
|
||||
#define DPI_COLOR_CODING_16BIT_2 0x1
|
||||
#define DPI_COLOR_CODING_16BIT_3 0x2
|
||||
@@ -61,22 +62,25 @@
|
||||
#define OUTVACT_LPCMD_TIME(p) (((p) & 0xff) << 16)
|
||||
#define INVACT_LPCMD_TIME(p) ((p) & 0xff)
|
||||
|
||||
#define DSI_DBI_VCID 0x1c
|
||||
#define DSI_DBI_CFG 0x20
|
||||
#define DSI_DBI_PARTITIONING_EN 0x24
|
||||
#define DSI_DBI_CMDSIZE 0x28
|
||||
|
||||
#define DSI_PCKHDL_CFG 0x2c
|
||||
#define EN_CRC_RX BIT(4)
|
||||
#define EN_ECC_RX BIT(3)
|
||||
#define EN_BTA BIT(2)
|
||||
#define EN_EOTP_RX BIT(1)
|
||||
#define EN_EOTP_TX BIT(0)
|
||||
#define CRC_RX_EN BIT(4)
|
||||
#define ECC_RX_EN BIT(3)
|
||||
#define BTA_EN BIT(2)
|
||||
#define EOTP_RX_EN BIT(1)
|
||||
#define EOTP_TX_EN BIT(0)
|
||||
|
||||
#define DSI_GEN_VCID 0x30
|
||||
|
||||
#define DSI_MODE_CFG 0x34
|
||||
#define ENABLE_VIDEO_MODE 0
|
||||
#define ENABLE_CMD_MODE BIT(0)
|
||||
|
||||
#define DSI_VID_MODE_CFG 0x38
|
||||
#define FRAME_BTA_ACK BIT(14)
|
||||
#define ENABLE_LOW_POWER (0x3f << 8)
|
||||
#define ENABLE_LOW_POWER_MASK (0x3f << 8)
|
||||
#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES 0x0
|
||||
@@ -85,8 +89,13 @@
|
||||
#define VID_MODE_TYPE_MASK 0x3
|
||||
|
||||
#define DSI_VID_PKT_SIZE 0x3c
|
||||
#define VID_PKT_SIZE(p) (((p) & 0x3fff) << 0)
|
||||
#define VID_PKT_MAX_SIZE 0x3fff
|
||||
#define VID_PKT_SIZE(p) ((p) & 0x3fff)
|
||||
|
||||
#define DSI_VID_NUM_CHUNKS 0x40
|
||||
#define VID_NUM_CHUNKS(c) ((c) & 0x1fff)
|
||||
|
||||
#define DSI_VID_NULL_SIZE 0x44
|
||||
#define VID_NULL_SIZE(b) ((b) & 0x1fff)
|
||||
|
||||
#define DSI_VID_HSA_TIME 0x48
|
||||
#define DSI_VID_HBP_TIME 0x4c
|
||||
@@ -95,6 +104,8 @@
|
||||
#define DSI_VID_VBP_LINES 0x58
|
||||
#define DSI_VID_VFP_LINES 0x5c
|
||||
#define DSI_VID_VACTIVE_LINES 0x60
|
||||
#define DSI_EDPI_CMD_SIZE 0x64
|
||||
|
||||
#define DSI_CMD_MODE_CFG 0x68
|
||||
#define MAX_RD_PKT_SIZE_LP BIT(24)
|
||||
#define DCS_LW_TX_LP BIT(19)
|
||||
@@ -108,8 +119,8 @@
|
||||
#define GEN_SW_2P_TX_LP BIT(10)
|
||||
#define GEN_SW_1P_TX_LP BIT(9)
|
||||
#define GEN_SW_0P_TX_LP BIT(8)
|
||||
#define EN_ACK_RQST BIT(1)
|
||||
#define EN_TEAR_FX BIT(0)
|
||||
#define ACK_RQST_EN BIT(1)
|
||||
#define TEAR_FX_EN BIT(0)
|
||||
|
||||
#define CMD_MODE_ALL_LP (MAX_RD_PKT_SIZE_LP | \
|
||||
DCS_LW_TX_LP | \
|
||||
@@ -125,27 +136,31 @@
|
||||
GEN_SW_0P_TX_LP)
|
||||
|
||||
#define DSI_GEN_HDR 0x6c
|
||||
/* TODO These 2 defines will be reworked thanks to mipi_dsi_create_packet() */
|
||||
#define GEN_HDATA(data) (((data) & 0xffff) << 8)
|
||||
#define GEN_HDATA_MASK (0xffff << 8)
|
||||
#define GEN_HTYPE(type) (((type) & 0xff) << 0)
|
||||
#define GEN_HTYPE_MASK 0xff
|
||||
|
||||
#define DSI_GEN_PLD_DATA 0x70
|
||||
|
||||
#define DSI_CMD_PKT_STATUS 0x74
|
||||
#define GEN_CMD_EMPTY BIT(0)
|
||||
#define GEN_CMD_FULL BIT(1)
|
||||
#define GEN_PLD_W_EMPTY BIT(2)
|
||||
#define GEN_PLD_W_FULL BIT(3)
|
||||
#define GEN_PLD_R_EMPTY BIT(4)
|
||||
#define GEN_PLD_R_FULL BIT(5)
|
||||
#define GEN_RD_CMD_BUSY BIT(6)
|
||||
#define GEN_PLD_R_FULL BIT(5)
|
||||
#define GEN_PLD_R_EMPTY BIT(4)
|
||||
#define GEN_PLD_W_FULL BIT(3)
|
||||
#define GEN_PLD_W_EMPTY BIT(2)
|
||||
#define GEN_CMD_FULL BIT(1)
|
||||
#define GEN_CMD_EMPTY BIT(0)
|
||||
|
||||
#define DSI_TO_CNT_CFG 0x78
|
||||
#define HSTX_TO_CNT(p) (((p) & 0xffff) << 16)
|
||||
#define LPRX_TO_CNT(p) ((p) & 0xffff)
|
||||
|
||||
#define DSI_HS_RD_TO_CNT 0x7c
|
||||
#define DSI_LP_RD_TO_CNT 0x80
|
||||
#define DSI_HS_WR_TO_CNT 0x84
|
||||
#define DSI_LP_WR_TO_CNT 0x88
|
||||
#define DSI_BTA_TO_CNT 0x8c
|
||||
|
||||
#define DSI_LPCLK_CTRL 0x94
|
||||
#define AUTO_CLKLANE_CTRL BIT(1)
|
||||
#define PHY_TXREQUESTCLKHS BIT(0)
|
||||
@@ -154,6 +169,7 @@
|
||||
#define PHY_CLKHS2LP_TIME(lbcc) (((lbcc) & 0x3ff) << 16)
|
||||
#define PHY_CLKLP2HS_TIME(lbcc) ((lbcc) & 0x3ff)
|
||||
|
||||
/* TODO Next register is slightly different between 1.30 & 1.31 IP version */
|
||||
#define DSI_PHY_TMR_CFG 0x9c
|
||||
#define PHY_HS2LP_TIME(lbcc) (((lbcc) & 0xff) << 24)
|
||||
#define PHY_LP2HS_TIME(lbcc) (((lbcc) & 0xff) << 16)
|
||||
@@ -170,12 +186,15 @@
|
||||
#define PHY_UNSHUTDOWNZ BIT(0)
|
||||
|
||||
#define DSI_PHY_IF_CFG 0xa4
|
||||
#define N_LANES(n) ((((n) - 1) & 0x3) << 0)
|
||||
#define PHY_STOP_WAIT_TIME(cycle) (((cycle) & 0xff) << 8)
|
||||
#define N_LANES(n) (((n) - 1) & 0x3)
|
||||
|
||||
#define DSI_PHY_ULPS_CTRL 0xa8
|
||||
#define DSI_PHY_TX_TRIGGERS 0xac
|
||||
|
||||
#define DSI_PHY_STATUS 0xb0
|
||||
#define LOCK BIT(0)
|
||||
#define STOP_STATE_CLK_LANE BIT(2)
|
||||
#define PHY_STOP_STATE_CLK_LANE BIT(2)
|
||||
#define PHY_LOCK BIT(0)
|
||||
|
||||
#define DSI_PHY_TST_CTRL0 0xb4
|
||||
#define PHY_TESTCLK BIT(1)
|
||||
@@ -187,12 +206,13 @@
|
||||
#define PHY_TESTEN BIT(16)
|
||||
#define PHY_UNTESTEN 0
|
||||
#define PHY_TESTDOUT(n) (((n) & 0xff) << 8)
|
||||
#define PHY_TESTDIN(n) (((n) & 0xff) << 0)
|
||||
#define PHY_TESTDIN(n) ((n) & 0xff)
|
||||
|
||||
#define DSI_INT_ST0 0xbc
|
||||
#define DSI_INT_ST1 0xc0
|
||||
#define DSI_INT_MSK0 0xc4
|
||||
#define DSI_INT_MSK1 0xc8
|
||||
#define DSI_PHY_TMR_RD_CFG 0xf4
|
||||
|
||||
#define PHY_STATUS_TIMEOUT_US 10000
|
||||
#define CMD_PKT_STATUS_TIMEOUT_US 20000
|
||||
@@ -201,7 +221,6 @@ struct dw_mipi_dsi {
|
||||
struct drm_bridge bridge;
|
||||
struct mipi_dsi_host dsi_host;
|
||||
struct drm_bridge *panel_bridge;
|
||||
bool is_panel_bridge;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
||||
@@ -277,7 +296,6 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
|
||||
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
|
||||
if (IS_ERR(bridge))
|
||||
return PTR_ERR(bridge);
|
||||
dsi->is_panel_bridge = true;
|
||||
}
|
||||
|
||||
dsi->panel_bridge = bridge;
|
||||
@@ -292,8 +310,7 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
|
||||
{
|
||||
struct dw_mipi_dsi *dsi = host_to_dsi(host);
|
||||
|
||||
if (dsi->is_panel_bridge)
|
||||
drm_panel_bridge_remove(dsi->panel_bridge);
|
||||
drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
|
||||
|
||||
drm_bridge_remove(&dsi->bridge);
|
||||
|
||||
@@ -307,7 +324,7 @@ static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
|
||||
u32 val = 0;
|
||||
|
||||
if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
|
||||
val |= EN_ACK_RQST;
|
||||
val |= ACK_RQST_EN;
|
||||
if (lpm)
|
||||
val |= CMD_MODE_ALL_LP;
|
||||
|
||||
@@ -506,8 +523,8 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
|
||||
* timeout clock division should be computed with the
|
||||
* high speed transmission counter timeout and byte lane...
|
||||
*/
|
||||
dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
|
||||
TX_ESC_CLK_DIVIDSION(esc_clk_division));
|
||||
dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
|
||||
TX_ESC_CLK_DIVISION(esc_clk_division));
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
||||
@@ -520,7 +537,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
||||
color = DPI_COLOR_CODING_24BIT;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666:
|
||||
color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
|
||||
color = DPI_COLOR_CODING_18BIT_2 | LOOSELY18_EN;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||
color = DPI_COLOR_CODING_18BIT_1;
|
||||
@@ -535,7 +552,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
val |= HSYNC_ACTIVE_LOW;
|
||||
|
||||
dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
|
||||
dsi_write(dsi, DSI_DPI_VCID, DPI_VCID(dsi->channel));
|
||||
dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
|
||||
dsi_write(dsi, DSI_DPI_CFG_POL, val);
|
||||
/*
|
||||
@@ -550,7 +567,7 @@ static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
|
||||
|
||||
static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
|
||||
{
|
||||
dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
|
||||
dsi_write(dsi, DSI_PCKHDL_CFG, CRC_RX_EN | ECC_RX_EN | BTA_EN);
|
||||
}
|
||||
|
||||
static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
|
||||
@@ -571,7 +588,7 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
|
||||
/*
|
||||
* TODO dw drv improvements
|
||||
* compute high speed transmission counter timeout according
|
||||
* to the timeout clock division (TO_CLK_DIVIDSION) and byte lane...
|
||||
* to the timeout clock division (TO_CLK_DIVISION) and byte lane...
|
||||
*/
|
||||
dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
|
||||
/*
|
||||
@@ -684,13 +701,13 @@ static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
|
||||
dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
|
||||
PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
|
||||
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
|
||||
val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
|
||||
val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0)
|
||||
DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
|
||||
|
||||
ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
|
||||
val, val & STOP_STATE_CLK_LANE, 1000,
|
||||
val, val & PHY_STOP_STATE_CLK_LANE, 1000,
|
||||
PHY_STATUS_TIMEOUT_US);
|
||||
if (ret < 0)
|
||||
DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
|
||||
@@ -865,15 +882,14 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
|
||||
* Note that the reset was not defined in the initial device tree, so
|
||||
* we have to be prepared for it not being found.
|
||||
*/
|
||||
apb_rst = devm_reset_control_get(dev, "apb");
|
||||
apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb");
|
||||
if (IS_ERR(apb_rst)) {
|
||||
ret = PTR_ERR(apb_rst);
|
||||
if (ret == -ENOENT) {
|
||||
apb_rst = NULL;
|
||||
} else {
|
||||
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Unable to get reset control: %d\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (apb_rst) {
|
||||
|
||||
Reference in New Issue
Block a user