Commit d0f6d401 authored by Dave Airlie's avatar Dave Airlie

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

More 4.15 drm-misc stuff:

Cross-subsystem Changes:
- bridge cleanup refactor (Benjamin Gaignard)

Core Changes:
- less surprising atomic iterators (Maarten), fixes an oops introduced
  in drm-next
- better gem/fb helper docs (Noralf)
- fix dma-buf rcu races (Christian König)

Driver Changes:
- adv7511: CEC support (Hans Verkuil)
- sun4i update from Chen-Yu to improve hdmi and A31 support
- sii8620: add remote control support (Maceiej Purski)

New drivers:
- SiI9234 bridge driver (Maciej Purski)
- 7" rpi touch panel (Eric Anholt)

Note that this contains a topic pull from regmap, needed by the sun4i
changes. Mark Brown sent that out for pulling into drm-misc.

* tag 'drm-misc-next-2017-10-12' of git://anongit.freedesktop.org/drm/drm-misc: (29 commits)
  drm/dp: WARN about invalid/unknown link rates and bw codes
  drm/msm/mdp5: remove less than 0 comparison for unsigned value
  drm/bridge/sii8620: add remote control support
  drm/sun4i: hdmi: Add support for A31's HDMI controller
  drm/sun4i: hdmi: Add A31 specific DDC register definitions
  drm/sun4i: hdmi: Add support for controller hardware variants
  dt-bindings: display: sun4i: Add binding for A31 HDMI controller
  drm/sun4i: hdmi: Allow using second PLL as TMDS clk parent
  drm/sun4i: hdmi: create a regmap for later use
  drm/sun4i: hdmi: Disable clks in bind function error path and unbind function
  drm/sun4i: tcon: Add support for demuxing TCON output on A31
  drm/sun4i: tcon: Add variant callback for TCON output muxing
  drm/bridge/synopsys: dsi :remove is_panel_bridge
  drm/vc4: remove bridge from driver internal structure
  drm/stm: ltdc: remove bridge from driver internal structure
  drm/drm_of: add drm_of_panel_bridge_remove function
  drm/bridge: make drm_panel_bridge_remove more robust
  dma-fence: fix dma_fence_get_rcu_safe v2
  dma-buf: make reservation_object_copy_fences rcu save
  drm/atomic: Unref duplicated drm_atomic_state in drm_atomic_helper_resume()
  ...
parents 25e1a798 cccf4e3f
...@@ -68,6 +68,8 @@ Optional properties: ...@@ -68,6 +68,8 @@ Optional properties:
- adi,disable-timing-generator: Only for ADV7533. Disables the internal timing - 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, generator. The chip will rely on the sync signals in the DSI data lanes,
rather than generate its own timings for HDMI output. rather than generate its own timings for HDMI output.
- clocks: from common clock binding: reference to the CEC clock.
- clock-names: from common clock binding: must be "cec".
Required nodes: Required nodes:
...@@ -89,6 +91,8 @@ Example ...@@ -89,6 +91,8 @@ Example
reg = <39>; reg = <39>;
interrupt-parent = <&gpio3>; interrupt-parent = <&gpio3>;
interrupts = <29 IRQ_TYPE_EDGE_FALLING>; interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
clocks = <&cec_clock>;
clock-names = "cec";
adi,input-depth = <8>; adi,input-depth = <8>;
adi,input-colorspace = "rgb"; adi,input-colorspace = "rgb";
......
Silicon Image SiI9234 HDMI/MHL bridge bindings
Required properties:
- compatible : "sil,sii9234".
- reg : I2C address for TPI interface, use 0x39
- avcc33-supply : MHL/USB Switch Supply Voltage (3.3V)
- iovcc18-supply : I/O Supply Voltage (1.8V)
- avcc12-supply : TMDS Analog Supply Voltage (1.2V)
- cvcc12-supply : Digital Core Supply Voltage (1.2V)
- interrupts, interrupt-parent: interrupt specifier of INT pin
- reset-gpios: gpio specifier of RESET pin (active low)
- video interfaces: Device node can contain two video interface port
nodes for HDMI encoder and connector according to [1].
- port@0 - MHL to HDMI
- port@1 - MHL to connector
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
Example:
sii9234@39 {
compatible = "sil,sii9234";
reg = <0x39>;
avcc33-supply = <&vcc33mhl>;
iovcc18-supply = <&vcc18mhl>;
avcc12-supply = <&vsil12>;
cvcc12-supply = <&vsil12>;
reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpf3>;
interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mhl_to_hdmi: endpoint {
remote-endpoint = <&hdmi_to_mhl>;
};
};
port@1 {
reg = <1>;
mhl_to_connector: endpoint {
remote-endpoint = <&connector_to_mhl>;
};
};
};
};
This binding covers the official 7" (800x480) Raspberry Pi touchscreen
panel.
This DSI panel contains:
- TC358762 DSI->DPI bridge
- Atmel microcontroller on I2C for power sequencing the DSI bridge and
controlling backlight
- Touchscreen controller on I2C for touch input
and this binding covers the DSI display parts but not its touch input.
Required properties:
- compatible: Must be "raspberrypi,7inch-touchscreen-panel"
- reg: Must be "45"
- port: See panel-common.txt
Example:
dsi1: dsi@7e700000 {
#address-cells = <1>;
#size-cells = <0>;
<...>
port {
dsi_out_port: endpoint {
remote-endpoint = <&panel_dsi_port>;
};
};
};
i2c_dsi: i2c {
compatible = "i2c-gpio";
#address-cells = <1>;
#size-cells = <0>;
gpios = <&gpio 28 0
&gpio 29 0>;
lcd@45 {
compatible = "raspberrypi,7inch-touchscreen-panel";
reg = <0x45>;
port {
panel_dsi_port: endpoint {
remote-endpoint = <&dsi_out_port>;
};
};
};
};
...@@ -41,14 +41,17 @@ CEC. It is one end of the pipeline. ...@@ -41,14 +41,17 @@ CEC. It is one end of the pipeline.
Required properties: Required properties:
- compatible: value must be one of: - compatible: value must be one of:
* allwinner,sun5i-a10s-hdmi * allwinner,sun5i-a10s-hdmi
* allwinner,sun6i-a31-hdmi
- reg: base address and size of memory-mapped region - reg: base address and size of memory-mapped region
- interrupts: interrupt associated to this IP - interrupts: interrupt associated to this IP
- clocks: phandles to the clocks feeding the HDMI encoder - clocks: phandles to the clocks feeding the HDMI encoder
* ahb: the HDMI interface clock * ahb: the HDMI interface clock
* mod: the HDMI module clock * mod: the HDMI module clock
* ddc: the HDMI ddc clock (A31 only)
* pll-0: the first video PLL * pll-0: the first video PLL
* pll-1: the second video PLL * pll-1: the second video PLL
- clock-names: the clock names mentioned above - clock-names: the clock names mentioned above
- resets: phandle to the reset control for the HDMI encoder (A31 only)
- dmas: phandles to the DMA channels used by the HDMI encoder - dmas: phandles to the DMA channels used by the HDMI encoder
* ddc-tx: The channel for DDC transmission * ddc-tx: The channel for DDC transmission
* ddc-rx: The channel for DDC reception * ddc-rx: The channel for DDC reception
......
...@@ -266,8 +266,7 @@ EXPORT_SYMBOL(reservation_object_add_excl_fence); ...@@ -266,8 +266,7 @@ EXPORT_SYMBOL(reservation_object_add_excl_fence);
* @dst: the destination reservation object * @dst: the destination reservation object
* @src: the source reservation object * @src: the source reservation object
* *
* Copy all fences from src to dst. Both src->lock as well as dst-lock must be * Copy all fences from src to dst. dst-lock must be held.
* held.
*/ */
int reservation_object_copy_fences(struct reservation_object *dst, int reservation_object_copy_fences(struct reservation_object *dst,
struct reservation_object *src) struct reservation_object *src)
...@@ -277,33 +276,62 @@ int reservation_object_copy_fences(struct reservation_object *dst, ...@@ -277,33 +276,62 @@ int reservation_object_copy_fences(struct reservation_object *dst,
size_t size; size_t size;
unsigned i; unsigned i;
src_list = reservation_object_get_list(src); rcu_read_lock();
src_list = rcu_dereference(src->fence);
retry:
if (src_list) { if (src_list) {
size = offsetof(typeof(*src_list), unsigned shared_count = src_list->shared_count;
shared[src_list->shared_count]);
size = offsetof(typeof(*src_list), shared[shared_count]);
rcu_read_unlock();
dst_list = kmalloc(size, GFP_KERNEL); dst_list = kmalloc(size, GFP_KERNEL);
if (!dst_list) if (!dst_list)
return -ENOMEM; return -ENOMEM;
dst_list->shared_count = src_list->shared_count; rcu_read_lock();
dst_list->shared_max = src_list->shared_count; src_list = rcu_dereference(src->fence);
for (i = 0; i < src_list->shared_count; ++i) if (!src_list || src_list->shared_count > shared_count) {
dst_list->shared[i] = kfree(dst_list);
dma_fence_get(src_list->shared[i]); goto retry;
}
dst_list->shared_count = 0;
dst_list->shared_max = shared_count;
for (i = 0; i < src_list->shared_count; ++i) {
struct dma_fence *fence;
fence = rcu_dereference(src_list->shared[i]);
if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
&fence->flags))
continue;
if (!dma_fence_get_rcu(fence)) {
kfree(dst_list);
src_list = rcu_dereference(src->fence);
goto retry;
}
if (dma_fence_is_signaled(fence)) {
dma_fence_put(fence);
continue;
}
dst_list->shared[dst_list->shared_count++] = fence;
}
} else { } else {
dst_list = NULL; dst_list = NULL;
} }
new = dma_fence_get_rcu_safe(&src->fence_excl);
rcu_read_unlock();
kfree(dst->staged); kfree(dst->staged);
dst->staged = NULL; dst->staged = NULL;
src_list = reservation_object_get_list(dst); src_list = reservation_object_get_list(dst);
old = reservation_object_get_excl(dst); old = reservation_object_get_excl(dst);
new = reservation_object_get_excl(src);
dma_fence_get(new);
preempt_disable(); preempt_disable();
write_seqcount_begin(&dst->seq); write_seqcount_begin(&dst->seq);
......
...@@ -71,7 +71,7 @@ config DRM_PARADE_PS8622 ...@@ -71,7 +71,7 @@ config DRM_PARADE_PS8622
config DRM_SIL_SII8620 config DRM_SIL_SII8620
tristate "Silicon Image SII8620 HDMI/MHL bridge" tristate "Silicon Image SII8620 HDMI/MHL bridge"
depends on OF depends on OF && RC_CORE
select DRM_KMS_HELPER select DRM_KMS_HELPER
help help
Silicon Image SII8620 HDMI/MHL bridge chip driver. Silicon Image SII8620 HDMI/MHL bridge chip driver.
...@@ -84,6 +84,14 @@ config DRM_SII902X ...@@ -84,6 +84,14 @@ config DRM_SII902X
---help--- ---help---
Silicon Image sii902x bridge chip driver. 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 config DRM_TOSHIBA_TC358767
tristate "Toshiba TC358767 eDP bridge" tristate "Toshiba TC358767 eDP bridge"
depends on OF depends on OF
......
...@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o ...@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_SII902X) += sii902x.o
obj-$(CONFIG_DRM_SII9234) += sii9234.o
obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
......
...@@ -21,3 +21,11 @@ config DRM_I2C_ADV7533 ...@@ -21,3 +21,11 @@ config DRM_I2C_ADV7533
default y default y
help help
Support for the Analog Devices ADV7533 DSI to HDMI encoder. 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.
adv7511-y := adv7511_drv.o adv7511-y := adv7511_drv.o
adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.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 adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
...@@ -195,6 +195,25 @@ ...@@ -195,6 +195,25 @@
#define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x) #define ADV7511_PACKET_GM(x) ADV7511_PACKET(5, x)
#define ADV7511_PACKET_SPARE(x) ADV7511_PACKET(6, 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 { enum adv7511_input_clock {
ADV7511_INPUT_CLOCK_1X, ADV7511_INPUT_CLOCK_1X,
ADV7511_INPUT_CLOCK_2X, ADV7511_INPUT_CLOCK_2X,
...@@ -297,6 +316,8 @@ enum adv7511_type { ...@@ -297,6 +316,8 @@ enum adv7511_type {
ADV7533, ADV7533,
}; };
#define ADV7511_MAX_ADDRS 3
struct adv7511 { struct adv7511 {
struct i2c_client *i2c_main; struct i2c_client *i2c_main;
struct i2c_client *i2c_edid; struct i2c_client *i2c_edid;
...@@ -341,15 +362,27 @@ struct adv7511 { ...@@ -341,15 +362,27 @@ struct adv7511 {
enum adv7511_type type; enum adv7511_type type;
struct platform_device *audio_pdev; 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 #ifdef CONFIG_DRM_I2C_ADV7533
void adv7533_dsi_power_on(struct adv7511 *adv); void adv7533_dsi_power_on(struct adv7511 *adv);
void adv7533_dsi_power_off(struct adv7511 *adv); void adv7533_dsi_power_off(struct adv7511 *adv);
void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode); void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode);
int adv7533_patch_registers(struct adv7511 *adv); int adv7533_patch_registers(struct adv7511 *adv);
void adv7533_uninit_cec(struct adv7511 *adv); int adv7533_patch_cec_registers(struct adv7511 *adv);
int adv7533_init_cec(struct adv7511 *adv);
int adv7533_attach_dsi(struct adv7511 *adv); int adv7533_attach_dsi(struct adv7511 *adv);
void adv7533_detach_dsi(struct adv7511 *adv); void adv7533_detach_dsi(struct adv7511 *adv);
int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv); int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
...@@ -372,11 +405,7 @@ static inline int adv7533_patch_registers(struct adv7511 *adv) ...@@ -372,11 +405,7 @@ static inline int adv7533_patch_registers(struct adv7511 *adv)
return -ENODEV; return -ENODEV;
} }
static inline void adv7533_uninit_cec(struct adv7511 *adv) static inline int adv7533_patch_cec_registers(struct adv7511 *adv)
{
}
static inline int adv7533_init_cec(struct adv7511 *adv)
{ {
return -ENODEV; return -ENODEV;
} }
......
/*
* 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 @@ ...@@ -11,12 +11,15 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/clk.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <media/cec.h>
#include "adv7511.h" #include "adv7511.h"
/* ADI recommended values for proper operation. */ /* ADI recommended values for proper operation. */
...@@ -336,8 +339,10 @@ static void __adv7511_power_on(struct adv7511 *adv7511) ...@@ -336,8 +339,10 @@ static void __adv7511_power_on(struct adv7511 *adv7511)
*/ */
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0), regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD); ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1), regmap_update_bits(adv7511->regmap,
ADV7511_INT1_DDC_ERROR); ADV7511_REG_INT_ENABLE(1),
ADV7511_INT1_DDC_ERROR,
ADV7511_INT1_DDC_ERROR);
} }
/* /*
...@@ -373,6 +378,9 @@ static void __adv7511_power_off(struct adv7511 *adv7511) ...@@ -373,6 +378,9 @@ static void __adv7511_power_off(struct adv7511 *adv7511)
regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER, regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
ADV7511_POWER_POWER_DOWN, ADV7511_POWER_POWER_DOWN,
ADV7511_POWER_POWER_DOWN); ADV7511_POWER_POWER_DOWN);
regmap_update_bits(adv7511->regmap,
ADV7511_REG_INT_ENABLE(1),
ADV7511_INT1_DDC_ERROR, 0);
regcache_mark_dirty(adv7511->regmap); regcache_mark_dirty(adv7511->regmap);
} }
...@@ -423,6 +431,8 @@ static void adv7511_hpd_work(struct work_struct *work) ...@@ -423,6 +431,8 @@ static void adv7511_hpd_work(struct work_struct *work)
if (adv7511->connector.status != status) { if (adv7511->connector.status != status) {
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); drm_kms_helper_hotplug_event(adv7511->connector.dev);
} }
} }
...@@ -453,6 +463,10 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd) ...@@ -453,6 +463,10 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
wake_up_all(&adv7511->wq); wake_up_all(&adv7511->wq);
} }
#ifdef CONFIG_DRM_I2C_ADV7511_CEC
adv7511_cec_irq_process(adv7511, irq1);
#endif
return 0; return 0;
} }
...@@ -595,6 +609,8 @@ static int adv7511_get_modes(struct adv7511 *adv7511, ...@@ -595,6 +609,8 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
kfree(edid); kfree(edid);
cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
return count; return count;
} }
...@@ -919,6 +935,65 @@ static void adv7511_uninit_regulators(struct adv7511 *adv) ...@@ -919,6 +935,65 @@ static void adv7511_uninit_regulators(struct adv7511 *adv)
regulator_bulk_disable(adv->num_supplies, adv->supplies); 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, static int adv7511_parse_dt(struct device_node *np,
struct adv7511_link_config *config) struct adv7511_link_config *config)
{ {
...@@ -1009,6 +1084,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ...@@ -1009,6 +1084,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
struct device *dev = &i2c->dev; struct device *dev = &i2c->dev;
unsigned int main_i2c_addr = i2c->addr << 1; unsigned int main_i2c_addr = i2c->addr << 1;
unsigned int edid_i2c_addr = main_i2c_addr + 4; unsigned int edid_i2c_addr = main_i2c_addr + 4;
unsigned int offset;
unsigned int val; unsigned int val;
int ret; int ret;
...@@ -1092,11 +1168,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ...@@ -1092,11 +1168,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto uninit_regulators; goto uninit_regulators;
} }
if (adv7511->type == ADV7533) { ret = adv7511_init_cec_regmap(adv7511);
ret = adv7533_init_cec(adv7511); if (ret)
if (ret) goto err_i2c_unregister_edid;
goto err_i2c_unregister_edid;
}
INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work); 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) ...@@ -1111,10 +1185,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
goto err_unregister_cec; 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); adv7511_power_off(adv7511);
i2c_set_clientdata(i2c, adv7511); i2c_set_clientdata(i2c, adv7511);
...@@ -1129,10 +1199,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ...@@ -1129,10 +1199,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
adv7511_audio_init(dev, adv7511); 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; return 0;
err_unregister_cec: 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: err_i2c_unregister_edid:
i2c_unregister_device(adv7511->i2c_edid); i2c_unregister_device(adv7511->i2c_edid);
uninit_regulators: uninit_regulators:
...@@ -1145,10 +1228,11 @@ static int adv7511_remove(struct i2c_client *i2c) ...@@ -1145,10 +1228,11 @@ static int adv7511_remove(struct i2c_client *i2c)
{ {
struct adv7511 *adv7511 = i2c_get_clientdata(i2c); struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
if (adv7511->type == ADV7533) { if (adv7511->type == ADV7533)
adv7533_detach_dsi(adv7511); 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); adv7511_uninit_regulators(adv7511);
...@@ -1156,6 +1240,8 @@ static int adv7511_remove(struct i2c_client *i2c) ...@@ -1156,6 +1240,8 @@ static int adv7511_remove(struct i2c_client *i2c)
adv7511_audio_exit(adv7511); adv7511_audio_exit(adv7511);
cec_unregister_adapter(adv7511->cec_adap);
i2c_unregister_device(adv7511->i2c_edid); i2c_unregister_device(adv7511->i2c_edid);
return 0; return 0;
......
...@@ -32,14 +32,6 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = { ...@@ -32,14 +32,6 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = {
{ 0x05, 0xc8 }, { 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) static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
{ {
struct mipi_dsi_device *dsi = adv->dsi; struct mipi_dsi_device *dsi = adv->dsi;
...@@ -145,37 +137,11 @@ int adv7533_patch_registers(struct adv7511 *adv) ...@@ -145,37 +137,11 @@ int adv7533_patch_registers(struct adv7511 *adv)
ARRAY_SIZE(adv7533_fixed_registers)); 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; return regmap_register_patch(adv->regmap_cec,
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,
adv7533_cec_fixed_registers, adv7533_cec_fixed_registers,
ARRAY_SIZE(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) int adv7533_attach_dsi(struct adv7511 *adv)
......
...@@ -188,7 +188,15 @@ EXPORT_SYMBOL(drm_panel_bridge_add); ...@@ -188,7 +188,15 @@ EXPORT_SYMBOL(drm_panel_bridge_add);
*/ */
void drm_panel_bridge_remove(struct drm_bridge *bridge) 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); drm_bridge_remove(bridge);
devm_kfree(panel_bridge->panel->dev, bridge); devm_kfree(panel_bridge->panel->dev, bridge);
......
This diff is collapsed.
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <media/rc-core.h>
#include "sil-sii8620.h" #include "sil-sii8620.h"
#define SII8620_BURST_BUF_LEN 288 #define SII8620_BURST_BUF_LEN 288
...@@ -58,6 +60,7 @@ enum sii8620_mt_state { ...@@ -58,6 +60,7 @@ enum sii8620_mt_state {
struct sii8620 { struct sii8620 {
struct drm_bridge bridge; struct drm_bridge bridge;
struct device *dev; struct device *dev;
struct rc_dev *rc_dev;
struct clk *clk_xtal; struct clk *clk_xtal;
struct gpio_desc *gpio_reset; struct gpio_desc *gpio_reset;
struct gpio_desc *gpio_int; struct gpio_desc *gpio_int;
...@@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code) ...@@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, 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, static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
struct sii8620_mt_msg *msg) struct sii8620_mt_msg *msg)
{ {
...@@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx) ...@@ -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)); 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) static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
{ {
u8 ints[MHL_INT_SIZE]; u8 ints[MHL_INT_SIZE];
...@@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx) ...@@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx)
static void sii8620_msc_mr_msc_msg(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]; u8 buf[2];
if (!msg)
return;
sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2); sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
switch (buf[0]) { switch (buf[0]) {
case MHL_MSC_MSG_RAPK: case MHL_MSC_MSG_RAPK:
msg = sii8620_msc_msg_first(ctx);
if (!msg)
return;
msg->ret = buf[1]; msg->ret = buf[1];
ctx->mt_state = MT_STATE_DONE; ctx->mt_state = MT_STATE_DONE;
break; 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: default:
dev_err(ctx->dev, "%s message type %d,%d not supported", dev_err(ctx->dev, "%s message type %d,%d not supported",
__func__, buf[0], buf[1]); __func__, buf[0], buf[1]);
...@@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx) ...@@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx)
enable_irq(to_i2c_client(ctx->dev)->irq); 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) static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
{ {
return container_of(bridge, struct sii8620, 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, static bool sii8620_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
...@@ -2151,6 +2235,8 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, ...@@ -2151,6 +2235,8 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge,
} }
static const struct drm_bridge_funcs sii8620_bridge_funcs = { static const struct drm_bridge_funcs sii8620_bridge_funcs = {
.attach = sii8620_attach,
.detach = sii8620_detach,
.mode_fixup = sii8620_mode_fixup, .mode_fixup = sii8620_mode_fixup,
}; };
...@@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client) ...@@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client)
struct sii8620 *ctx = i2c_get_clientdata(client); struct sii8620 *ctx = i2c_get_clientdata(client);
disable_irq(to_i2c_client(ctx->dev)->irq); disable_irq(to_i2c_client(ctx->dev)->irq);
drm_bridge_remove(&ctx->bridge);
sii8620_hw_off(ctx); sii8620_hw_off(ctx);
drm_bridge_remove(&ctx->bridge);
return 0; return 0;
} }
......
...@@ -221,7 +221,6 @@ struct dw_mipi_dsi { ...@@ -221,7 +221,6 @@ struct dw_mipi_dsi {
struct drm_bridge bridge; struct drm_bridge bridge;
struct mipi_dsi_host dsi_host; struct mipi_dsi_host dsi_host;
struct drm_bridge *panel_bridge; struct drm_bridge *panel_bridge;
bool is_panel_bridge;
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
...@@ -297,7 +296,6 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, ...@@ -297,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); bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
if (IS_ERR(bridge)) if (IS_ERR(bridge))
return PTR_ERR(bridge); return PTR_ERR(bridge);
dsi->is_panel_bridge = true;
} }
dsi->panel_bridge = bridge; dsi->panel_bridge = bridge;
...@@ -312,8 +310,7 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host, ...@@ -312,8 +310,7 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
{ {
struct dw_mipi_dsi *dsi = host_to_dsi(host); struct dw_mipi_dsi *dsi = host_to_dsi(host);
if (dsi->is_panel_bridge) drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
drm_panel_bridge_remove(dsi->panel_bridge);
drm_bridge_remove(&dsi->bridge); drm_bridge_remove(&dsi->bridge);
......
...@@ -182,9 +182,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) ...@@ -182,9 +182,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
for (i = 0; i < state->num_private_objs; i++) { for (i = 0; i < state->num_private_objs; i++) {
struct drm_private_obj *obj = state->private_objs[i].ptr; struct drm_private_obj *obj = state->private_objs[i].ptr;
if (!obj)
continue;
obj->funcs->atomic_destroy_state(obj, obj->funcs->atomic_destroy_state(obj,
state->private_objs[i].state); state->private_objs[i].state);
state->private_objs[i].ptr = NULL; state->private_objs[i].ptr = NULL;
......
...@@ -3052,6 +3052,7 @@ int drm_atomic_helper_resume(struct drm_device *dev, ...@@ -3052,6 +3052,7 @@ int drm_atomic_helper_resume(struct drm_device *dev,
drm_modeset_backoff(&ctx); drm_modeset_backoff(&ctx);
} }
drm_atomic_state_put(state);
drm_modeset_drop_locks(&ctx); drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx); drm_modeset_acquire_fini(&ctx);
......
...@@ -137,8 +137,10 @@ EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); ...@@ -137,8 +137,10 @@ EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
u8 drm_dp_link_rate_to_bw_code(int link_rate) u8 drm_dp_link_rate_to_bw_code(int link_rate)
{ {
switch (link_rate) { switch (link_rate) {
case 162000:
default: default:
WARN(1, "unknown DP link rate %d, using %x\n", link_rate,
DP_LINK_BW_1_62);
case 162000:
return DP_LINK_BW_1_62; return DP_LINK_BW_1_62;
case 270000: case 270000:
return DP_LINK_BW_2_7; return DP_LINK_BW_2_7;
...@@ -151,8 +153,9 @@ EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); ...@@ -151,8 +153,9 @@ EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
int drm_dp_bw_code_to_link_rate(u8 link_bw) int drm_dp_bw_code_to_link_rate(u8 link_bw)
{ {
switch (link_bw) { switch (link_bw) {
case DP_LINK_BW_1_62:
default: default:
WARN(1, "unknown DP link BW code %x, using 162000\n", link_bw);
case DP_LINK_BW_1_62:
return 162000; return 162000;
case DP_LINK_BW_2_7: case DP_LINK_BW_2_7:
return 270000; return 270000;
......
...@@ -27,19 +27,24 @@ ...@@ -27,19 +27,24 @@
* DOC: overview * DOC: overview
* *
* This library provides helpers for drivers that don't subclass * This library provides helpers for drivers that don't subclass
* &drm_framebuffer and and use &drm_gem_object for their backing storage. * &drm_framebuffer and use &drm_gem_object for their backing storage.
* *
* Drivers without additional needs to validate framebuffers can simply use * Drivers without additional needs to validate framebuffers can simply use
* drm_gem_fb_create() and everything is wired up automatically. But all * drm_gem_fb_create() and everything is wired up automatically. Other drivers
* parts can be used individually. * can use all parts independently.
*/ */
/** /**
* drm_gem_fb_get_obj() - Get GEM object for framebuffer * drm_gem_fb_get_obj() - Get GEM object backing the framebuffer
* @fb: The framebuffer * @fb: Framebuffer
* @plane: Which plane * @plane: Plane index
* *
* Returns the GEM object for given framebuffer. * No additional reference is taken beyond the one that the &drm_frambuffer
* already holds.
*
* Returns:
* Pointer to &drm_gem_object for the given framebuffer and plane index or NULL
* if it does not exist.
*/ */
struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb, struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
unsigned int plane) unsigned int plane)
...@@ -82,7 +87,7 @@ drm_gem_fb_alloc(struct drm_device *dev, ...@@ -82,7 +87,7 @@ drm_gem_fb_alloc(struct drm_device *dev,
/** /**
* drm_gem_fb_destroy - Free GEM backed framebuffer * drm_gem_fb_destroy - Free GEM backed framebuffer
* @fb: DRM framebuffer * @fb: Framebuffer
* *
* Frees a GEM backed framebuffer with its backing buffer(s) and the structure * Frees a GEM backed framebuffer with its backing buffer(s) and the structure
* itself. Drivers can use this as their &drm_framebuffer_funcs->destroy * itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
...@@ -102,12 +107,13 @@ EXPORT_SYMBOL(drm_gem_fb_destroy); ...@@ -102,12 +107,13 @@ EXPORT_SYMBOL(drm_gem_fb_destroy);
/** /**
* drm_gem_fb_create_handle - Create handle for GEM backed framebuffer * drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
* @fb: DRM framebuffer * @fb: Framebuffer
* @file: drm file * @file: DRM file to register the handle for
* @handle: handle created * @handle: Pointer to return the created handle
* *
* This function creates a handle for the GEM object backing the framebuffer.
* Drivers can use this as their &drm_framebuffer_funcs->create_handle * Drivers can use this as their &drm_framebuffer_funcs->create_handle
* callback. * callback. The GETFB IOCTL calls into this callback.
* *
* Returns: * Returns:
* 0 on success or a negative error code on failure. * 0 on success or a negative error code on failure.
...@@ -120,18 +126,21 @@ int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file, ...@@ -120,18 +126,21 @@ int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
EXPORT_SYMBOL(drm_gem_fb_create_handle); EXPORT_SYMBOL(drm_gem_fb_create_handle);
/** /**
* drm_gem_fb_create_with_funcs() - helper function for the * drm_gem_fb_create_with_funcs() - Helper function for the
* &drm_mode_config_funcs.fb_create * &drm_mode_config_funcs.fb_create
* callback * callback
* @dev: DRM device * @dev: DRM device
* @file: drm file for the ioctl call * @file: DRM file that holds the GEM handle(s) backing the framebuffer
* @mode_cmd: metadata from the userspace fb creation request * @mode_cmd: Metadata from the userspace framebuffer creation request
* @funcs: vtable to be used for the new framebuffer object * @funcs: vtable to be used for the new framebuffer object
* *
* This can be used to set &drm_framebuffer_funcs for drivers that need the * This can be used to set &drm_framebuffer_funcs for drivers that need the
* &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't * &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
* need to change &drm_framebuffer_funcs. * need to change &drm_framebuffer_funcs.
* The function does buffer size validation. * The function does buffer size validation.
*
* Returns:
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
*/ */
struct drm_framebuffer * struct drm_framebuffer *
drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file, drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
...@@ -192,15 +201,26 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = { ...@@ -192,15 +201,26 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
}; };
/** /**
* drm_gem_fb_create() - &drm_mode_config_funcs.fb_create callback function * drm_gem_fb_create() - Helper function for the
* &drm_mode_config_funcs.fb_create callback
* @dev: DRM device * @dev: DRM device
* @file: drm file for the ioctl call * @file: DRM file that holds the GEM handle(s) backing the framebuffer
* @mode_cmd: metadata from the userspace fb creation request * @mode_cmd: Metadata from the userspace framebuffer creation request
*
* This function creates a new framebuffer object described by
* &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
* backing the framebuffer.
* *
* If your hardware has special alignment or pitch requirements these should be * If your hardware has special alignment or pitch requirements these should be
* checked before calling this function. The function does buffer size * checked before calling this function. The function does buffer size
* validation. Use drm_gem_fb_create_with_funcs() if you need to set * validation. Use drm_gem_fb_create_with_funcs() if you need to set
* &drm_framebuffer_funcs.dirty. * &drm_framebuffer_funcs.dirty.
*
* Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
* The ADDFB2 IOCTL calls into this callback.
*
* Returns:
* Pointer to a &drm_framebuffer on success or an error pointer on failure.
*/ */
struct drm_framebuffer * struct drm_framebuffer *
drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
...@@ -212,15 +232,15 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file, ...@@ -212,15 +232,15 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
EXPORT_SYMBOL_GPL(drm_gem_fb_create); EXPORT_SYMBOL_GPL(drm_gem_fb_create);
/** /**
* drm_gem_fb_prepare_fb() - Prepare gem framebuffer * drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
* @plane: Which plane * @plane: Plane
* @state: Plane state attach fence to * @state: Plane state the fence will be attached to
* *
* This can be used as the &drm_plane_helper_funcs.prepare_fb hook. * This function prepares a GEM backed framebuffer for scanout by checking if
* * the plane framebuffer has a DMA-BUF attached. If it does, it extracts the
* This function checks if the plane FB has an dma-buf attached, extracts * exclusive fence and attaches it to the plane state for the atomic helper to
* the exclusive fence and attaches it to plane state for the atomic helper * wait on. This function can be used as the &drm_plane_helper_funcs.prepare_fb
* to wait on. * callback.
* *
* There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple
* gem based framebuffer drivers which have their buffers always pinned in * gem based framebuffer drivers which have their buffers always pinned in
...@@ -246,17 +266,19 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, ...@@ -246,17 +266,19 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb); EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
/** /**
* drm_gem_fbdev_fb_create - Create a drm_framebuffer for fbdev emulation * drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
* emulation
* @dev: DRM device * @dev: DRM device
* @sizes: fbdev size description * @sizes: fbdev size description
* @pitch_align: optional pitch alignment * @pitch_align: Optional pitch alignment
* @obj: GEM object backing the framebuffer * @obj: GEM object backing the framebuffer
* @funcs: vtable to be used for the new framebuffer object * @funcs: vtable to be used for the new framebuffer object
* *
* This function creates a framebuffer for use with fbdev emulation. * This function creates a framebuffer from a &drm_fb_helper_surface_size
* description for use in the &drm_fb_helper_funcs.fb_probe callback.
* *
* Returns: * Returns:
* Pointer to a drm_framebuffer on success or an error pointer on failure. * Pointer to a &drm_framebuffer on success or an error pointer on failure.
*/ */
struct drm_framebuffer * struct drm_framebuffer *
drm_gem_fbdev_fb_create(struct drm_device *dev, drm_gem_fbdev_fb_create(struct drm_device *dev,
......
...@@ -262,3 +262,36 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, ...@@ -262,3 +262,36 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
#ifdef CONFIG_DRM_PANEL_BRIDGE
/*
* drm_of_panel_bridge_remove - remove panel bridge
* @np: device tree node containing panel bridge output ports
*
* Remove the panel bridge of a given DT node's port and endpoint number
*
* Returns zero if successful, or one of the standard error codes if it fails.
*/
int drm_of_panel_bridge_remove(const struct device_node *np,
int port, int endpoint)
{
struct drm_bridge *bridge;
struct device_node *remote;
remote = of_graph_get_remote_node(np, port, endpoint);
if (!remote)
return -ENODEV;
bridge = of_drm_find_bridge(remote);
drm_panel_bridge_remove(bridge);
return 0;
}
#else
int drm_of_panel_bridge_remove(const struct device_node *np,
int port, int endpoint)
{
return -EINVAL;
}
#endif
EXPORT_SYMBOL_GPL(drm_of_panel_bridge_remove);
...@@ -599,7 +599,7 @@ static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe) ...@@ -599,7 +599,7 @@ static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
struct drm_crtc *crtc; struct drm_crtc *crtc;
struct drm_encoder *encoder; struct drm_encoder *encoder;
if (pipe < 0 || pipe >= priv->num_crtcs) if (pipe >= priv->num_crtcs)
return 0; return 0;
crtc = priv->crtcs[pipe]; crtc = priv->crtcs[pipe];
......
...@@ -82,6 +82,14 @@ config DRM_PANEL_PANASONIC_VVX10F034N00 ...@@ -82,6 +82,14 @@ config DRM_PANEL_PANASONIC_VVX10F034N00
WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some WUXGA (1920x1200) Novatek NT1397-based DSI panel as found in some
Xperia Z2 tablets Xperia Z2 tablets
config DRM_PANEL_RASPBERRYPI_TOUCHSCREEN
tristate "Raspberry Pi 7-inch touchscreen panel"
depends on DRM_MIPI_DSI
help
Say Y here if you want to enable support for the Raspberry
Pi 7" Touchscreen. To compile this driver as a module,
choose M here.
config DRM_PANEL_SAMSUNG_S6E3HA2 config DRM_PANEL_SAMSUNG_S6E3HA2
tristate "Samsung S6E3HA2 DSI video mode panel" tristate "Samsung S6E3HA2 DSI video mode panel"
depends on OF depends on OF
......
...@@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o ...@@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
......
This diff is collapsed.
...@@ -791,9 +791,8 @@ static const struct drm_encoder_funcs ltdc_encoder_funcs = { ...@@ -791,9 +791,8 @@ static const struct drm_encoder_funcs ltdc_encoder_funcs = {
.destroy = drm_encoder_cleanup, .destroy = drm_encoder_cleanup,
}; };
static int ltdc_encoder_init(struct drm_device *ddev) static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
{ {
struct ltdc_device *ldev = ddev->dev_private;
struct drm_encoder *encoder; struct drm_encoder *encoder;
int ret; int ret;
...@@ -807,7 +806,7 @@ static int ltdc_encoder_init(struct drm_device *ddev) ...@@ -807,7 +806,7 @@ static int ltdc_encoder_init(struct drm_device *ddev)
drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs, drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs,
DRM_MODE_ENCODER_DPI, NULL); DRM_MODE_ENCODER_DPI, NULL);
ret = drm_bridge_attach(encoder, ldev->bridge, NULL); ret = drm_bridge_attach(encoder, bridge, NULL);
if (ret) { if (ret) {
drm_encoder_cleanup(encoder); drm_encoder_cleanup(encoder);
return -EINVAL; return -EINVAL;
...@@ -936,12 +935,9 @@ int ltdc_load(struct drm_device *ddev) ...@@ -936,12 +935,9 @@ int ltdc_load(struct drm_device *ddev)
ret = PTR_ERR(bridge); ret = PTR_ERR(bridge);
goto err; goto err;
} }
ldev->is_panel_bridge = true;
} }
ldev->bridge = bridge; ret = ltdc_encoder_init(ddev, bridge);
ret = ltdc_encoder_init(ddev);
if (ret) { if (ret) {
DRM_ERROR("Failed to init encoder\n"); DRM_ERROR("Failed to init encoder\n");
goto err; goto err;
...@@ -972,8 +968,7 @@ int ltdc_load(struct drm_device *ddev) ...@@ -972,8 +968,7 @@ int ltdc_load(struct drm_device *ddev)
return 0; return 0;
err: err:
if (ldev->is_panel_bridge) drm_panel_bridge_remove(bridge);
drm_panel_bridge_remove(bridge);
clk_disable_unprepare(ldev->pixel_clk); clk_disable_unprepare(ldev->pixel_clk);
...@@ -986,8 +981,7 @@ void ltdc_unload(struct drm_device *ddev) ...@@ -986,8 +981,7 @@ void ltdc_unload(struct drm_device *ddev)
DRM_DEBUG_DRIVER("\n"); DRM_DEBUG_DRIVER("\n");
if (ldev->is_panel_bridge) drm_of_panel_bridge_remove(ddev->dev->of_node, 0, 0);
drm_panel_bridge_remove(ldev->bridge);
clk_disable_unprepare(ldev->pixel_clk); clk_disable_unprepare(ldev->pixel_clk);
} }
......
...@@ -24,8 +24,6 @@ struct ltdc_device { ...@@ -24,8 +24,6 @@ struct ltdc_device {
struct drm_fbdev_cma *fbdev; struct drm_fbdev_cma *fbdev;
void __iomem *regs; void __iomem *regs;
struct clk *pixel_clk; /* lcd pixel clock */ struct clk *pixel_clk; /* lcd pixel clock */
struct drm_bridge *bridge;
bool is_panel_bridge;
struct mutex err_lock; /* protecting error_status */ struct mutex err_lock; /* protecting error_status */
struct ltdc_caps caps; struct ltdc_caps caps;
u32 error_status; u32 error_status;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <drm/drm_connector.h> #include <drm/drm_connector.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
#include <linux/regmap.h>
#include <media/cec-pin.h> #include <media/cec-pin.h>
...@@ -58,10 +59,13 @@ ...@@ -58,10 +59,13 @@
#define SUN4I_HDMI_PAD_CTRL0_TXEN BIT(23) #define SUN4I_HDMI_PAD_CTRL0_TXEN BIT(23)
#define SUN4I_HDMI_PAD_CTRL1_REG 0x204 #define SUN4I_HDMI_PAD_CTRL1_REG 0x204
#define SUN4I_HDMI_PAD_CTRL1_UNKNOWN BIT(24) /* set on A31 */
#define SUN4I_HDMI_PAD_CTRL1_AMP_OPT BIT(23) #define SUN4I_HDMI_PAD_CTRL1_AMP_OPT BIT(23)
#define SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT BIT(22) #define SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT BIT(22)
#define SUN4I_HDMI_PAD_CTRL1_EMP_OPT BIT(20) #define SUN4I_HDMI_PAD_CTRL1_EMP_OPT BIT(20)
#define SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT BIT(19) #define SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT BIT(19)
#define SUN4I_HDMI_PAD_CTRL1_PWSCK BIT(18)
#define SUN4I_HDMI_PAD_CTRL1_PWSDT BIT(17)
#define SUN4I_HDMI_PAD_CTRL1_REG_DEN BIT(15) #define SUN4I_HDMI_PAD_CTRL1_REG_DEN BIT(15)
#define SUN4I_HDMI_PAD_CTRL1_REG_DENCK BIT(14) #define SUN4I_HDMI_PAD_CTRL1_REG_DENCK BIT(14)
#define SUN4I_HDMI_PAD_CTRL1_REG_EMP(n) (((n) & 7) << 10) #define SUN4I_HDMI_PAD_CTRL1_REG_EMP(n) (((n) & 7) << 10)
...@@ -152,21 +156,106 @@ ...@@ -152,21 +156,106 @@
#define SUN4I_HDMI_DDC_FIFO_SIZE 16 #define SUN4I_HDMI_DDC_FIFO_SIZE 16
/* A31 specific */
#define SUN6I_HDMI_DDC_CTRL_REG 0x500
#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31)
#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27)
#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6)
#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4)
#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0)
#define SUN6I_HDMI_DDC_CMD_REG 0x508
#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16)
/* command types in lower 3 bits are the same as sun4i */
#define SUN6I_HDMI_DDC_ADDR_REG 0x50c
#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24)
#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16)
#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8)
#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1)
#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514
#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8)
/* lower 8 bits are the same as sun4i */
#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518
#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15)
/* lower 9 bits are the same as sun4i */
#define SUN6I_HDMI_DDC_CLK_REG 0x520
/* DDC CLK bit fields are the same, but the formula is not */
#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580
enum sun4i_hdmi_pkt_type { enum sun4i_hdmi_pkt_type {
SUN4I_HDMI_PKT_AVI = 2, SUN4I_HDMI_PKT_AVI = 2,
SUN4I_HDMI_PKT_END = 15, SUN4I_HDMI_PKT_END = 15,
}; };
struct sun4i_hdmi_variant {
bool has_ddc_parent_clk;
bool has_reset_control;
u32 pad_ctrl0_init_val;
u32 pad_ctrl1_init_val;
u32 pll_ctrl_init_val;
struct reg_field ddc_clk_reg;
u8 ddc_clk_pre_divider;
u8 ddc_clk_m_offset;
u8 tmds_clk_div_offset;
/* Register fields for I2C adapter */
struct reg_field field_ddc_en;
struct reg_field field_ddc_start;
struct reg_field field_ddc_reset;
struct reg_field field_ddc_addr_reg;
struct reg_field field_ddc_slave_addr;
struct reg_field field_ddc_int_mask;
struct reg_field field_ddc_int_status;
struct reg_field field_ddc_fifo_clear;
struct reg_field field_ddc_fifo_rx_thres;
struct reg_field field_ddc_fifo_tx_thres;
struct reg_field field_ddc_byte_count;
struct reg_field field_ddc_cmd;
struct reg_field field_ddc_sda_en;
struct reg_field field_ddc_sck_en;
/* DDC FIFO register offset */
u32 ddc_fifo_reg;
/*
* DDC FIFO threshold boundary conditions
*
* This is used to cope with the threshold boundary condition
* being slightly different on sun5i and sun6i.
*
* On sun5i the threshold is exclusive, i.e. does not include,
* the value of the threshold. ( > for RX; < for TX )
* On sun6i the threshold is inclusive, i.e. includes, the
* value of the threshold. ( >= for RX; <= for TX )
*/
bool ddc_fifo_thres_incl;
bool ddc_fifo_has_dir;
};
struct sun4i_hdmi { struct sun4i_hdmi {
struct drm_connector connector; struct drm_connector connector;
struct drm_encoder encoder; struct drm_encoder encoder;
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
struct regmap *regmap;
/* Reset control */
struct reset_control *reset;
/* Parent clocks */ /* Parent clocks */
struct clk *bus_clk; struct clk *bus_clk;
struct clk *mod_clk; struct clk *mod_clk;
struct clk *ddc_parent_clk;
struct clk *pll0_clk; struct clk *pll0_clk;
struct clk *pll1_clk; struct clk *pll1_clk;
...@@ -176,10 +265,28 @@ struct sun4i_hdmi { ...@@ -176,10 +265,28 @@ struct sun4i_hdmi {
struct i2c_adapter *i2c; struct i2c_adapter *i2c;
/* Regmap fields for I2C adapter */
struct regmap_field *field_ddc_en;
struct regmap_field *field_ddc_start;
struct regmap_field *field_ddc_reset;
struct regmap_field *field_ddc_addr_reg;
struct regmap_field *field_ddc_slave_addr;
struct regmap_field *field_ddc_int_mask;
struct regmap_field *field_ddc_int_status;
struct regmap_field *field_ddc_fifo_clear;
struct regmap_field *field_ddc_fifo_rx_thres;
struct regmap_field *field_ddc_fifo_tx_thres;
struct regmap_field *field_ddc_byte_count;
struct regmap_field *field_ddc_cmd;
struct regmap_field *field_ddc_sda_en;
struct regmap_field *field_ddc_sck_en;
struct sun4i_drv *drv; struct sun4i_drv *drv;
bool hdmi_monitor; bool hdmi_monitor;
struct cec_adapter *cec_adap; struct cec_adapter *cec_adap;
const struct sun4i_hdmi_variant *variant;
}; };
int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk); int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/regmap.h>
#include "sun4i_tcon.h" #include "sun4i_tcon.h"
#include "sun4i_hdmi.h" #include "sun4i_hdmi.h"
...@@ -18,6 +19,9 @@ ...@@ -18,6 +19,9 @@
struct sun4i_ddc { struct sun4i_ddc {
struct clk_hw hw; struct clk_hw hw;
struct sun4i_hdmi *hdmi; struct sun4i_hdmi *hdmi;
struct regmap_field *reg;
u8 pre_div;
u8 m_offset;
}; };
static inline struct sun4i_ddc *hw_to_ddc(struct clk_hw *hw) static inline struct sun4i_ddc *hw_to_ddc(struct clk_hw *hw)
...@@ -27,6 +31,8 @@ static inline struct sun4i_ddc *hw_to_ddc(struct clk_hw *hw) ...@@ -27,6 +31,8 @@ static inline struct sun4i_ddc *hw_to_ddc(struct clk_hw *hw)
static unsigned long sun4i_ddc_calc_divider(unsigned long rate, static unsigned long sun4i_ddc_calc_divider(unsigned long rate,
unsigned long parent_rate, unsigned long parent_rate,
const u8 pre_div,
const u8 m_offset,
u8 *m, u8 *n) u8 *m, u8 *n)
{ {
unsigned long best_rate = 0; unsigned long best_rate = 0;
...@@ -36,7 +42,8 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate, ...@@ -36,7 +42,8 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate,
for (_n = 0; _n < 8; _n++) { for (_n = 0; _n < 8; _n++) {
unsigned long tmp_rate; unsigned long tmp_rate;
tmp_rate = (((parent_rate / 2) / 10) >> _n) / (_m + 1); tmp_rate = (((parent_rate / pre_div) / 10) >> _n) /
(_m + m_offset);
if (tmp_rate > rate) if (tmp_rate > rate)
continue; continue;
...@@ -60,21 +67,25 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate, ...@@ -60,21 +67,25 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate,
static long sun4i_ddc_round_rate(struct clk_hw *hw, unsigned long rate, static long sun4i_ddc_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate) unsigned long *prate)
{ {
return sun4i_ddc_calc_divider(rate, *prate, NULL, NULL); struct sun4i_ddc *ddc = hw_to_ddc(hw);
return sun4i_ddc_calc_divider(rate, *prate, ddc->pre_div,
ddc->m_offset, NULL, NULL);
} }
static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw, static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct sun4i_ddc *ddc = hw_to_ddc(hw); struct sun4i_ddc *ddc = hw_to_ddc(hw);
u32 reg; unsigned int reg;
u8 m, n; u8 m, n;
reg = readl(ddc->hdmi->base + SUN4I_HDMI_DDC_CLK_REG); regmap_field_read(ddc->reg, &reg);
m = (reg >> 3) & 0x7; m = (reg >> 3) & 0xf;
n = reg & 0x7; n = reg & 0x7;
return (((parent_rate / 2) / 10) >> n) / (m + 1); return (((parent_rate / ddc->pre_div) / 10) >> n) /
(m + ddc->m_offset);
} }
static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate, static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate,
...@@ -83,10 +94,12 @@ static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -83,10 +94,12 @@ static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate,
struct sun4i_ddc *ddc = hw_to_ddc(hw); struct sun4i_ddc *ddc = hw_to_ddc(hw);
u8 div_m, div_n; u8 div_m, div_n;
sun4i_ddc_calc_divider(rate, parent_rate, &div_m, &div_n); sun4i_ddc_calc_divider(rate, parent_rate, ddc->pre_div,
ddc->m_offset, &div_m, &div_n);
writel(SUN4I_HDMI_DDC_CLK_M(div_m) | SUN4I_HDMI_DDC_CLK_N(div_n), regmap_field_write(ddc->reg,
ddc->hdmi->base + SUN4I_HDMI_DDC_CLK_REG); SUN4I_HDMI_DDC_CLK_M(div_m) |
SUN4I_HDMI_DDC_CLK_N(div_n));
return 0; return 0;
} }
...@@ -111,6 +124,11 @@ int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent) ...@@ -111,6 +124,11 @@ int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent)
if (!ddc) if (!ddc)
return -ENOMEM; return -ENOMEM;
ddc->reg = devm_regmap_field_alloc(hdmi->dev, hdmi->regmap,
hdmi->variant->ddc_clk_reg);
if (IS_ERR(ddc->reg))
return PTR_ERR(ddc->reg);
init.name = "hdmi-ddc"; init.name = "hdmi-ddc";
init.ops = &sun4i_ddc_ops; init.ops = &sun4i_ddc_ops;
init.parent_names = &parent_name; init.parent_names = &parent_name;
...@@ -118,6 +136,8 @@ int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent) ...@@ -118,6 +136,8 @@ int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent)
ddc->hdmi = hdmi; ddc->hdmi = hdmi;
ddc->hw.init = &init; ddc->hw.init = &init;
ddc->pre_div = hdmi->variant->ddc_clk_pre_divider;
ddc->m_offset = hdmi->variant->ddc_clk_m_offset;
hdmi->ddc_clk = devm_clk_register(hdmi->dev, &ddc->hw); hdmi->ddc_clk = devm_clk_register(hdmi->dev, &ddc->hw);
if (IS_ERR(hdmi->ddc_clk)) if (IS_ERR(hdmi->ddc_clk))
......
This diff is collapsed.
This diff is collapsed.
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
struct sun4i_tmds { struct sun4i_tmds {
struct clk_hw hw; struct clk_hw hw;
struct sun4i_hdmi *hdmi; struct sun4i_hdmi *hdmi;
u8 div_offset;
}; };
static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw) static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw)
...@@ -28,6 +30,7 @@ static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw) ...@@ -28,6 +30,7 @@ static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw)
static unsigned long sun4i_tmds_calc_divider(unsigned long rate, static unsigned long sun4i_tmds_calc_divider(unsigned long rate,
unsigned long parent_rate, unsigned long parent_rate,
u8 div_offset,
u8 *div, u8 *div,
bool *half) bool *half)
{ {
...@@ -35,7 +38,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, ...@@ -35,7 +38,7 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate,
u8 best_m = 0, m; u8 best_m = 0, m;
bool is_double; bool is_double;
for (m = 1; m < 16; m++) { for (m = div_offset ?: 1; m < (16 + div_offset); m++) {
u8 d; u8 d;
for (d = 1; d < 3; d++) { for (d = 1; d < 3; d++) {
...@@ -67,11 +70,12 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate, ...@@ -67,11 +70,12 @@ static unsigned long sun4i_tmds_calc_divider(unsigned long rate,
static int sun4i_tmds_determine_rate(struct clk_hw *hw, static int sun4i_tmds_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req) struct clk_rate_request *req)
{ {
struct clk_hw *parent; struct sun4i_tmds *tmds = hw_to_tmds(hw);
struct clk_hw *parent = NULL;
unsigned long best_parent = 0; unsigned long best_parent = 0;
unsigned long rate = req->rate; unsigned long rate = req->rate;
int best_div = 1, best_half = 1; int best_div = 1, best_half = 1;
int i, j; int i, j, p;
/* /*
* We only consider PLL3, since the TCON is very likely to be * We only consider PLL3, since the TCON is very likely to be
...@@ -79,32 +83,38 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw, ...@@ -79,32 +83,38 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw,
* clock, so we should not need to do anything. * clock, so we should not need to do anything.
*/ */
parent = clk_hw_get_parent_by_index(hw, 0); for (p = 0; p < clk_hw_get_num_parents(hw); p++) {
if (!parent) parent = clk_hw_get_parent_by_index(hw, p);
return -EINVAL; if (!parent)
continue;
for (i = 1; i < 3; i++) {
for (j = 1; j < 16; j++) { for (i = 1; i < 3; i++) {
unsigned long ideal = rate * i * j; for (j = tmds->div_offset ?: 1;
unsigned long rounded; j < (16 + tmds->div_offset); j++) {
unsigned long ideal = rate * i * j;
rounded = clk_hw_round_rate(parent, ideal); unsigned long rounded;
if (rounded == ideal) { rounded = clk_hw_round_rate(parent, ideal);
best_parent = rounded;
best_half = i; if (rounded == ideal) {
best_div = j; best_parent = rounded;
goto out; best_half = i;
} best_div = j;
goto out;
if (abs(rate - rounded / i) < }
abs(rate - best_parent / best_div)) {
best_parent = rounded; if (abs(rate - rounded / i) <
best_div = i; abs(rate - best_parent / best_div)) {
best_parent = rounded;
best_div = i;
}
} }
} }
} }
if (!parent)
return -EINVAL;
out: out:
req->rate = best_parent / best_half / best_div; req->rate = best_parent / best_half / best_div;
req->best_parent_rate = best_parent; req->best_parent_rate = best_parent;
...@@ -124,7 +134,7 @@ static unsigned long sun4i_tmds_recalc_rate(struct clk_hw *hw, ...@@ -124,7 +134,7 @@ static unsigned long sun4i_tmds_recalc_rate(struct clk_hw *hw,
parent_rate /= 2; parent_rate /= 2;
reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
reg = (reg >> 4) & 0xf; reg = ((reg >> 4) & 0xf) + tmds->div_offset;
if (!reg) if (!reg)
reg = 1; reg = 1;
...@@ -139,7 +149,8 @@ static int sun4i_tmds_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -139,7 +149,8 @@ static int sun4i_tmds_set_rate(struct clk_hw *hw, unsigned long rate,
u32 reg; u32 reg;
u8 div; u8 div;
sun4i_tmds_calc_divider(rate, parent_rate, &div, &half); sun4i_tmds_calc_divider(rate, parent_rate, tmds->div_offset,
&div, &half);
reg = readl(tmds->hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG); reg = readl(tmds->hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG);
reg &= ~SUN4I_HDMI_PAD_CTRL1_HALVE_CLK; reg &= ~SUN4I_HDMI_PAD_CTRL1_HALVE_CLK;
...@@ -149,7 +160,7 @@ static int sun4i_tmds_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -149,7 +160,7 @@ static int sun4i_tmds_set_rate(struct clk_hw *hw, unsigned long rate,
reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); reg = readl(tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
reg &= ~SUN4I_HDMI_PLL_CTRL_DIV_MASK; reg &= ~SUN4I_HDMI_PLL_CTRL_DIV_MASK;
writel(reg | SUN4I_HDMI_PLL_CTRL_DIV(div), writel(reg | SUN4I_HDMI_PLL_CTRL_DIV(div - tmds->div_offset),
tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); tmds->hdmi->base + SUN4I_HDMI_PLL_CTRL_REG);
return 0; return 0;
...@@ -216,6 +227,7 @@ int sun4i_tmds_create(struct sun4i_hdmi *hdmi) ...@@ -216,6 +227,7 @@ int sun4i_tmds_create(struct sun4i_hdmi *hdmi)
tmds->hdmi = hdmi; tmds->hdmi = hdmi;
tmds->hw.init = &init; tmds->hw.init = &init;
tmds->div_offset = hdmi->variant->tmds_clk_div_offset;
hdmi->tmds_clk = devm_clk_register(hdmi->dev, &tmds->hw); hdmi->tmds_clk = devm_clk_register(hdmi->dev, &tmds->hw);
if (IS_ERR(hdmi->tmds_clk)) if (IS_ERR(hdmi->tmds_clk))
......
...@@ -14,9 +14,12 @@ ...@@ -14,9 +14,12 @@
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_modes.h> #include <drm/drm_modes.h>
#include <drm/drm_of.h> #include <drm/drm_of.h>
#include <uapi/drm/drm_mode.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -109,26 +112,37 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable) ...@@ -109,26 +112,37 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
} }
EXPORT_SYMBOL(sun4i_tcon_enable_vblank); EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, /*
struct drm_encoder *encoder) * This function is a helper for TCON output muxing. The TCON output
* muxing control register in earlier SoCs (without the TCON TOP block)
* are located in TCON0. This helper returns a pointer to TCON0's
* sun4i_tcon structure, or NULL if not found.
*/
static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
{ {
u32 val; struct sun4i_drv *drv = drm->dev_private;
struct sun4i_tcon *tcon;
if (!tcon->quirks->has_unknown_mux) list_for_each_entry(tcon, &drv->tcon_list, list)
return; if (tcon->id == 0)
return tcon;
if (channel != 1) dev_warn(drm->dev,
return; "TCON0 not found, display output muxing may not work\n");
if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC) return NULL;
val = 1; }
else
val = 0;
/* void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
* FIXME: Undocumented bits struct drm_encoder *encoder)
*/ {
regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val); int ret = -ENOTSUPP;
if (tcon->quirks->set_mux)
ret = tcon->quirks->set_mux(tcon, encoder);
DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",
encoder->name, encoder->crtc->name, ret);
} }
EXPORT_SYMBOL(sun4i_tcon_set_mux); EXPORT_SYMBOL(sun4i_tcon_set_mux);
...@@ -767,14 +781,57 @@ static int sun4i_tcon_remove(struct platform_device *pdev) ...@@ -767,14 +781,57 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
return 0; return 0;
} }
/* platform specific TCON muxing callbacks */
static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,
struct drm_encoder *encoder)
{
u32 val;
if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
val = 1;
else
val = 0;
/*
* FIXME: Undocumented bits
*/
return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
}
static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,
struct drm_encoder *encoder)
{
struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
u32 shift;
if (!tcon0)
return -EINVAL;
switch (encoder->encoder_type) {
case DRM_MODE_ENCODER_TMDS:
/* HDMI */
shift = 8;
break;
default:
/* TODO A31 has MIPI DSI but A31s does not */
return -EINVAL;
}
regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
0x3 << shift, tcon->id << shift);
return 0;
}
static const struct sun4i_tcon_quirks sun5i_a13_quirks = { static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
.has_unknown_mux = true, .has_channel_1 = true,
.has_channel_1 = true, .set_mux = sun5i_a13_tcon_set_mux,
}; };
static const struct sun4i_tcon_quirks sun6i_a31_quirks = { static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
.has_channel_1 = true, .has_channel_1 = true,
.needs_de_be_mux = true, .needs_de_be_mux = true,
.set_mux = sun6i_tcon_set_mux,
}; };
static const struct sun4i_tcon_quirks sun6i_a31s_quirks = { static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {
......
...@@ -145,10 +145,14 @@ ...@@ -145,10 +145,14 @@
#define SUN4I_TCON_MAX_CHANNELS 2 #define SUN4I_TCON_MAX_CHANNELS 2
struct sun4i_tcon;
struct sun4i_tcon_quirks { struct sun4i_tcon_quirks {
bool has_unknown_mux; /* sun5i has undocumented mux */
bool has_channel_1; /* a33 does not have channel 1 */ bool has_channel_1; /* a33 does not have channel 1 */
bool needs_de_be_mux; /* sun6i needs mux to select backend */ bool needs_de_be_mux; /* sun6i needs mux to select backend */
/* callback to handle tcon muxing options */
int (*set_mux)(struct sun4i_tcon *, struct drm_encoder *);
}; };
struct sun4i_tcon { struct sun4i_tcon {
......
...@@ -97,8 +97,6 @@ struct vc4_dpi { ...@@ -97,8 +97,6 @@ struct vc4_dpi {
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_connector *connector; struct drm_connector *connector;
struct drm_bridge *bridge;
bool is_panel_bridge;
void __iomem *regs; void __iomem *regs;
...@@ -251,10 +249,11 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) ...@@ -251,10 +249,11 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
{ {
struct device *dev = &dpi->pdev->dev; struct device *dev = &dpi->pdev->dev;
struct drm_panel *panel; struct drm_panel *panel;
struct drm_bridge *bridge;
int ret; int ret;
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
&panel, &dpi->bridge); &panel, &bridge);
if (ret) { if (ret) {
/* If nothing was connected in the DT, that's not an /* If nothing was connected in the DT, that's not an
* error. * error.
...@@ -265,13 +264,10 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) ...@@ -265,13 +264,10 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
return ret; return ret;
} }
if (panel) { if (panel)
dpi->bridge = drm_panel_bridge_add(panel, bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI);
DRM_MODE_CONNECTOR_DPI);
dpi->is_panel_bridge = true;
}
return drm_bridge_attach(dpi->encoder, dpi->bridge, NULL); return drm_bridge_attach(dpi->encoder, bridge, NULL);
} }
static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
...@@ -352,8 +348,7 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, ...@@ -352,8 +348,7 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_dpi *dpi = dev_get_drvdata(dev); struct vc4_dpi *dpi = dev_get_drvdata(dev);
if (dpi->is_panel_bridge) drm_of_panel_bridge_remove(dev->of_node, 0, 0);
drm_panel_bridge_remove(dpi->bridge);
drm_encoder_cleanup(dpi->encoder); drm_encoder_cleanup(dpi->encoder);
......
...@@ -262,6 +262,10 @@ enum { ...@@ -262,6 +262,10 @@ enum {
#define MHL_RAPK_UNSUPPORTED 0x02 /* Rcvd RAP action code not supported */ #define MHL_RAPK_UNSUPPORTED 0x02 /* Rcvd RAP action code not supported */
#define MHL_RAPK_BUSY 0x03 /* Responder too busy to respond */ #define MHL_RAPK_BUSY 0x03 /* Responder too busy to respond */
/* Bit masks for RCP messages */
#define MHL_RCP_KEY_RELEASED_MASK 0x80
#define MHL_RCP_KEY_ID_MASK 0x7F
/* /*
* Error status codes for RCPE messages * Error status codes for RCPE messages
*/ */
......
...@@ -585,12 +585,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -585,12 +585,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_oldnew_connector_in_state(__state, connector, old_connector_state, new_connector_state, __i) \ #define for_each_oldnew_connector_in_state(__state, connector, old_connector_state, new_connector_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->num_connector && \ (__i) < (__state)->num_connector; \
((connector) = (__state)->connectors[__i].ptr, \ (__i)++) \
(old_connector_state) = (__state)->connectors[__i].old_state, \ for_each_if ((__state)->connectors[__i].ptr && \
(new_connector_state) = (__state)->connectors[__i].new_state, 1); \ ((connector) = (__state)->connectors[__i].ptr, \
(__i)++) \ (old_connector_state) = (__state)->connectors[__i].old_state, \
for_each_if (connector) (new_connector_state) = (__state)->connectors[__i].new_state, 1))
/** /**
* for_each_old_connector_in_state - iterate over all connectors in an atomic update * for_each_old_connector_in_state - iterate over all connectors in an atomic update
...@@ -606,11 +606,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -606,11 +606,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_old_connector_in_state(__state, connector, old_connector_state, __i) \ #define for_each_old_connector_in_state(__state, connector, old_connector_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->num_connector && \ (__i) < (__state)->num_connector; \
((connector) = (__state)->connectors[__i].ptr, \ (__i)++) \
(old_connector_state) = (__state)->connectors[__i].old_state, 1); \ for_each_if ((__state)->connectors[__i].ptr && \
(__i)++) \ ((connector) = (__state)->connectors[__i].ptr, \
for_each_if (connector) (old_connector_state) = (__state)->connectors[__i].old_state, 1))
/** /**
* for_each_new_connector_in_state - iterate over all connectors in an atomic update * for_each_new_connector_in_state - iterate over all connectors in an atomic update
...@@ -626,11 +626,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -626,11 +626,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_new_connector_in_state(__state, connector, new_connector_state, __i) \ #define for_each_new_connector_in_state(__state, connector, new_connector_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->num_connector && \ (__i) < (__state)->num_connector; \
((connector) = (__state)->connectors[__i].ptr, \ (__i)++) \
(new_connector_state) = (__state)->connectors[__i].new_state, 1); \ for_each_if ((__state)->connectors[__i].ptr && \
(__i)++) \ ((connector) = (__state)->connectors[__i].ptr, \
for_each_if (connector) (new_connector_state) = (__state)->connectors[__i].new_state, 1))
/** /**
* for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update * for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update
...@@ -646,12 +646,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -646,12 +646,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_oldnew_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \ #define for_each_oldnew_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_crtc && \ (__i) < (__state)->dev->mode_config.num_crtc; \
((crtc) = (__state)->crtcs[__i].ptr, \
(old_crtc_state) = (__state)->crtcs[__i].old_state, \
(new_crtc_state) = (__state)->crtcs[__i].new_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (crtc) for_each_if ((__state)->crtcs[__i].ptr && \
((crtc) = (__state)->crtcs[__i].ptr, \
(old_crtc_state) = (__state)->crtcs[__i].old_state, \
(new_crtc_state) = (__state)->crtcs[__i].new_state, 1))
/** /**
* for_each_old_crtc_in_state - iterate over all CRTCs in an atomic update * for_each_old_crtc_in_state - iterate over all CRTCs in an atomic update
...@@ -666,11 +666,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -666,11 +666,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_old_crtc_in_state(__state, crtc, old_crtc_state, __i) \ #define for_each_old_crtc_in_state(__state, crtc, old_crtc_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_crtc && \ (__i) < (__state)->dev->mode_config.num_crtc; \
((crtc) = (__state)->crtcs[__i].ptr, \
(old_crtc_state) = (__state)->crtcs[__i].old_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (crtc) for_each_if ((__state)->crtcs[__i].ptr && \
((crtc) = (__state)->crtcs[__i].ptr, \
(old_crtc_state) = (__state)->crtcs[__i].old_state, 1))
/** /**
* for_each_new_crtc_in_state - iterate over all CRTCs in an atomic update * for_each_new_crtc_in_state - iterate over all CRTCs in an atomic update
...@@ -685,11 +685,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -685,11 +685,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_new_crtc_in_state(__state, crtc, new_crtc_state, __i) \ #define for_each_new_crtc_in_state(__state, crtc, new_crtc_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_crtc && \ (__i) < (__state)->dev->mode_config.num_crtc; \
((crtc) = (__state)->crtcs[__i].ptr, \
(new_crtc_state) = (__state)->crtcs[__i].new_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (crtc) for_each_if ((__state)->crtcs[__i].ptr && \
((crtc) = (__state)->crtcs[__i].ptr, \
(new_crtc_state) = (__state)->crtcs[__i].new_state, 1))
/** /**
* for_each_oldnew_plane_in_state - iterate over all planes in an atomic update * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update
...@@ -705,12 +705,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -705,12 +705,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_oldnew_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \ #define for_each_oldnew_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_total_plane && \ (__i) < (__state)->dev->mode_config.num_total_plane; \
((plane) = (__state)->planes[__i].ptr, \
(old_plane_state) = (__state)->planes[__i].old_state, \
(new_plane_state) = (__state)->planes[__i].new_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (plane) for_each_if ((__state)->planes[__i].ptr && \
((plane) = (__state)->planes[__i].ptr, \
(old_plane_state) = (__state)->planes[__i].old_state,\
(new_plane_state) = (__state)->planes[__i].new_state, 1))
/** /**
* for_each_old_plane_in_state - iterate over all planes in an atomic update * for_each_old_plane_in_state - iterate over all planes in an atomic update
...@@ -725,12 +725,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -725,12 +725,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_old_plane_in_state(__state, plane, old_plane_state, __i) \ #define for_each_old_plane_in_state(__state, plane, old_plane_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_total_plane && \ (__i) < (__state)->dev->mode_config.num_total_plane; \
((plane) = (__state)->planes[__i].ptr, \
(old_plane_state) = (__state)->planes[__i].old_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (plane) for_each_if ((__state)->planes[__i].ptr && \
((plane) = (__state)->planes[__i].ptr, \
(old_plane_state) = (__state)->planes[__i].old_state, 1))
/** /**
* for_each_new_plane_in_state - iterate over all planes in an atomic update * for_each_new_plane_in_state - iterate over all planes in an atomic update
* @__state: &struct drm_atomic_state pointer * @__state: &struct drm_atomic_state pointer
...@@ -744,11 +743,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -744,11 +743,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
*/ */
#define for_each_new_plane_in_state(__state, plane, new_plane_state, __i) \ #define for_each_new_plane_in_state(__state, plane, new_plane_state, __i) \
for ((__i) = 0; \ for ((__i) = 0; \
(__i) < (__state)->dev->mode_config.num_total_plane && \ (__i) < (__state)->dev->mode_config.num_total_plane; \
((plane) = (__state)->planes[__i].ptr, \
(new_plane_state) = (__state)->planes[__i].new_state, 1); \
(__i)++) \ (__i)++) \
for_each_if (plane) for_each_if ((__state)->planes[__i].ptr && \
((plane) = (__state)->planes[__i].ptr, \
(new_plane_state) = (__state)->planes[__i].new_state, 1))
/** /**
* for_each_oldnew_private_obj_in_state - iterate over all private objects in an atomic update * for_each_oldnew_private_obj_in_state - iterate over all private objects in an atomic update
...@@ -768,8 +767,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -768,8 +767,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
((obj) = (__state)->private_objs[__i].ptr, \ ((obj) = (__state)->private_objs[__i].ptr, \
(old_obj_state) = (__state)->private_objs[__i].old_state, \ (old_obj_state) = (__state)->private_objs[__i].old_state, \
(new_obj_state) = (__state)->private_objs[__i].new_state, 1); \ (new_obj_state) = (__state)->private_objs[__i].new_state, 1); \
(__i)++) \ (__i)++)
for_each_if (obj)
/** /**
* for_each_old_private_obj_in_state - iterate over all private objects in an atomic update * for_each_old_private_obj_in_state - iterate over all private objects in an atomic update
...@@ -787,8 +785,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -787,8 +785,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
(__i) < (__state)->num_private_objs && \ (__i) < (__state)->num_private_objs && \
((obj) = (__state)->private_objs[__i].ptr, \ ((obj) = (__state)->private_objs[__i].ptr, \
(old_obj_state) = (__state)->private_objs[__i].old_state, 1); \ (old_obj_state) = (__state)->private_objs[__i].old_state, 1); \
(__i)++) \ (__i)++)
for_each_if (obj)
/** /**
* for_each_new_private_obj_in_state - iterate over all private objects in an atomic update * for_each_new_private_obj_in_state - iterate over all private objects in an atomic update
...@@ -806,8 +803,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ...@@ -806,8 +803,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
(__i) < (__state)->num_private_objs && \ (__i) < (__state)->num_private_objs && \
((obj) = (__state)->private_objs[__i].ptr, \ ((obj) = (__state)->private_objs[__i].ptr, \
(new_obj_state) = (__state)->private_objs[__i].new_state, 1); \ (new_obj_state) = (__state)->private_objs[__i].new_state, 1); \
(__i)++) \ (__i)++)
for_each_if (obj)
/** /**
* drm_atomic_crtc_needs_modeset - compute combined modeset need * drm_atomic_crtc_needs_modeset - compute combined modeset need
......
...@@ -29,6 +29,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, ...@@ -29,6 +29,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
int port, int endpoint, int port, int endpoint,
struct drm_panel **panel, struct drm_panel **panel,
struct drm_bridge **bridge); struct drm_bridge **bridge);
int drm_of_panel_bridge_remove(const struct device_node *np,
int port, int endpoint);
#else #else
static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
struct device_node *port) struct device_node *port)
...@@ -65,6 +67,12 @@ static inline int drm_of_find_panel_or_bridge(const struct device_node *np, ...@@ -65,6 +67,12 @@ static inline int drm_of_find_panel_or_bridge(const struct device_node *np,
{ {
return -EINVAL; return -EINVAL;
} }
static inline int drm_of_panel_bridge_remove(const struct device_node *np,
int port, int endpoint)
{
return -EINVAL;
}
#endif #endif
static inline int drm_of_encoder_active_endpoint_id(struct device_node *node, static inline int drm_of_encoder_active_endpoint_id(struct device_node *node,
......
...@@ -248,9 +248,12 @@ dma_fence_get_rcu_safe(struct dma_fence * __rcu *fencep) ...@@ -248,9 +248,12 @@ dma_fence_get_rcu_safe(struct dma_fence * __rcu *fencep)
struct dma_fence *fence; struct dma_fence *fence;
fence = rcu_dereference(*fencep); fence = rcu_dereference(*fencep);
if (!fence || !dma_fence_get_rcu(fence)) if (!fence)
return NULL; return NULL;
if (!dma_fence_get_rcu(fence))
continue;
/* The atomic_inc_not_zero() inside dma_fence_get_rcu() /* The atomic_inc_not_zero() inside dma_fence_get_rcu()
* provides a full memory barrier upon success (such as now). * provides a full memory barrier upon success (such as now).
* This is paired with the write barrier from assigning * This is paired with the write barrier from assigning
......
...@@ -139,6 +139,45 @@ struct reg_sequence { ...@@ -139,6 +139,45 @@ struct reg_sequence {
pollret ?: ((cond) ? 0 : -ETIMEDOUT); \ pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
}) })
/**
* regmap_field_read_poll_timeout - Poll until a condition is met or timeout
*
* @field: Regmap field to read from
* @val: Unsigned integer variable to read the value into
* @cond: Break condition (usually involving @val)
* @sleep_us: Maximum time to sleep between reads in us (0
* tight-loops). Should be less than ~20ms since usleep_range
* is used (see Documentation/timers/timers-howto.txt).
* @timeout_us: Timeout in us, 0 means never timeout
*
* Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read
* error return value in case of a error read. In the two former cases,
* the last read value at @addr is stored in @val. Must not be called
* from atomic context if sleep_us or timeout_us are used.
*
* This is modelled after the readx_poll_timeout macros in linux/iopoll.h.
*/
#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_us) \
({ \
ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
int pollret; \
might_sleep_if(sleep_us); \
for (;;) { \
pollret = regmap_field_read((field), &(val)); \
if (pollret) \
break; \
if (cond) \
break; \
if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
pollret = regmap_field_read((field), &(val)); \
break; \
} \
if (sleep_us) \
usleep_range((sleep_us >> 2) + 1, sleep_us); \
} \
pollret ?: ((cond) ? 0 : -ETIMEDOUT); \
})
#ifdef CONFIG_REGMAP #ifdef CONFIG_REGMAP
enum regmap_endian { enum regmap_endian {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment