2020-11-02 04:14:35 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2018 , The Linux Foundation . All rights reserved .
* Copyright ( c ) 2019 - 2020. Linaro Limited .
*/
# include <linux/firmware.h>
# include <linux/gpio/consumer.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of_graph.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/regulator/consumer.h>
# include <linux/wait.h>
2021-01-22 02:33:03 +03:00
# include <linux/workqueue.h>
2020-11-02 04:14:35 +03:00
# include <sound/hdmi-codec.h>
# include <drm/drm_atomic_helper.h>
# include <drm/drm_bridge.h>
# include <drm/drm_mipi_dsi.h>
# include <drm/drm_print.h>
# include <drm/drm_probe_helper.h>
# define EDID_BLOCK_SIZE 128
# define EDID_NUM_BLOCKS 2
struct lt9611uxc {
struct device * dev ;
struct drm_bridge bridge ;
struct drm_connector connector ;
struct regmap * regmap ;
/* Protects all accesses to registers by stopping the on-chip MCU */
struct mutex ocm_lock ;
struct wait_queue_head wq ;
2021-01-22 02:33:03 +03:00
struct work_struct work ;
2020-11-02 04:14:35 +03:00
struct device_node * dsi0_node ;
struct device_node * dsi1_node ;
struct mipi_dsi_device * dsi0 ;
struct mipi_dsi_device * dsi1 ;
struct platform_device * audio_pdev ;
struct gpio_desc * reset_gpio ;
struct gpio_desc * enable_gpio ;
struct regulator_bulk_data supplies [ 2 ] ;
struct i2c_client * client ;
bool hpd_supported ;
bool edid_read ;
2021-01-22 02:33:03 +03:00
/* can be accessed from different threads, so protect this with ocm_lock */
bool hdmi_connected ;
2020-11-02 04:14:35 +03:00
uint8_t fw_version ;
} ;
# define LT9611_PAGE_CONTROL 0xff
static const struct regmap_range_cfg lt9611uxc_ranges [ ] = {
{
. name = " register_range " ,
. range_min = 0 ,
. range_max = 0xd0ff ,
. selector_reg = LT9611_PAGE_CONTROL ,
. selector_mask = 0xff ,
. selector_shift = 0 ,
. window_start = 0 ,
. window_len = 0x100 ,
} ,
} ;
static const struct regmap_config lt9611uxc_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xffff ,
. ranges = lt9611uxc_ranges ,
. num_ranges = ARRAY_SIZE ( lt9611uxc_ranges ) ,
} ;
struct lt9611uxc_mode {
u16 hdisplay ;
u16 vdisplay ;
u8 vrefresh ;
} ;
/*
* This chip supports only a fixed set of modes .
* Enumerate them here to check whether the mode is supported .
*/
static struct lt9611uxc_mode lt9611uxc_modes [ ] = {
{ 1920 , 1080 , 60 } ,
{ 1920 , 1080 , 30 } ,
{ 1920 , 1080 , 25 } ,
{ 1366 , 768 , 60 } ,
{ 1360 , 768 , 60 } ,
{ 1280 , 1024 , 60 } ,
{ 1280 , 800 , 60 } ,
{ 1280 , 720 , 60 } ,
{ 1280 , 720 , 50 } ,
{ 1280 , 720 , 30 } ,
{ 1152 , 864 , 60 } ,
{ 1024 , 768 , 60 } ,
{ 800 , 600 , 60 } ,
{ 720 , 576 , 50 } ,
{ 720 , 480 , 60 } ,
{ 640 , 480 , 60 } ,
} ;
static struct lt9611uxc * bridge_to_lt9611uxc ( struct drm_bridge * bridge )
{
return container_of ( bridge , struct lt9611uxc , bridge ) ;
}
static struct lt9611uxc * connector_to_lt9611uxc ( struct drm_connector * connector )
{
return container_of ( connector , struct lt9611uxc , connector ) ;
}
static void lt9611uxc_lock ( struct lt9611uxc * lt9611uxc )
{
mutex_lock ( & lt9611uxc - > ocm_lock ) ;
regmap_write ( lt9611uxc - > regmap , 0x80ee , 0x01 ) ;
}
static void lt9611uxc_unlock ( struct lt9611uxc * lt9611uxc )
{
regmap_write ( lt9611uxc - > regmap , 0x80ee , 0x00 ) ;
msleep ( 50 ) ;
mutex_unlock ( & lt9611uxc - > ocm_lock ) ;
}
static irqreturn_t lt9611uxc_irq_thread_handler ( int irq , void * dev_id )
{
struct lt9611uxc * lt9611uxc = dev_id ;
unsigned int irq_status = 0 ;
unsigned int hpd_status = 0 ;
lt9611uxc_lock ( lt9611uxc ) ;
regmap_read ( lt9611uxc - > regmap , 0xb022 , & irq_status ) ;
regmap_read ( lt9611uxc - > regmap , 0xb023 , & hpd_status ) ;
if ( irq_status )
regmap_write ( lt9611uxc - > regmap , 0xb022 , 0 ) ;
2021-01-22 02:33:01 +03:00
if ( irq_status & BIT ( 0 ) ) {
2020-11-02 04:14:35 +03:00
lt9611uxc - > edid_read = ! ! ( hpd_status & BIT ( 0 ) ) ;
2021-01-22 02:33:01 +03:00
wake_up_all ( & lt9611uxc - > wq ) ;
}
2020-11-02 04:14:35 +03:00
if ( irq_status & BIT ( 1 ) ) {
2021-01-22 02:33:03 +03:00
lt9611uxc - > hdmi_connected = hpd_status & BIT ( 1 ) ;
schedule_work ( & lt9611uxc - > work ) ;
2020-11-02 04:14:35 +03:00
}
2021-01-22 02:33:03 +03:00
lt9611uxc_unlock ( lt9611uxc ) ;
2020-11-02 04:14:35 +03:00
return IRQ_HANDLED ;
}
2021-01-22 02:33:03 +03:00
static void lt9611uxc_hpd_work ( struct work_struct * work )
{
struct lt9611uxc * lt9611uxc = container_of ( work , struct lt9611uxc , work ) ;
bool connected ;
2021-07-09 02:03:29 +03:00
if ( lt9611uxc - > connector . dev ) {
if ( lt9611uxc - > connector . dev - > mode_config . funcs )
drm_kms_helper_hotplug_event ( lt9611uxc - > connector . dev ) ;
} else {
2021-01-22 02:33:03 +03:00
mutex_lock ( & lt9611uxc - > ocm_lock ) ;
connected = lt9611uxc - > hdmi_connected ;
mutex_unlock ( & lt9611uxc - > ocm_lock ) ;
drm_bridge_hpd_notify ( & lt9611uxc - > bridge ,
connected ?
connector_status_connected :
connector_status_disconnected ) ;
}
}
2020-11-02 04:14:35 +03:00
static void lt9611uxc_reset ( struct lt9611uxc * lt9611uxc )
{
gpiod_set_value_cansleep ( lt9611uxc - > reset_gpio , 1 ) ;
msleep ( 20 ) ;
gpiod_set_value_cansleep ( lt9611uxc - > reset_gpio , 0 ) ;
msleep ( 20 ) ;
gpiod_set_value_cansleep ( lt9611uxc - > reset_gpio , 1 ) ;
msleep ( 300 ) ;
}
static void lt9611uxc_assert_5v ( struct lt9611uxc * lt9611uxc )
{
if ( ! lt9611uxc - > enable_gpio )
return ;
gpiod_set_value_cansleep ( lt9611uxc - > enable_gpio , 1 ) ;
msleep ( 20 ) ;
}
static int lt9611uxc_regulator_init ( struct lt9611uxc * lt9611uxc )
{
int ret ;
lt9611uxc - > supplies [ 0 ] . supply = " vdd " ;
lt9611uxc - > supplies [ 1 ] . supply = " vcc " ;
ret = devm_regulator_bulk_get ( lt9611uxc - > dev , 2 , lt9611uxc - > supplies ) ;
if ( ret < 0 )
return ret ;
return regulator_set_load ( lt9611uxc - > supplies [ 0 ] . consumer , 200000 ) ;
}
static int lt9611uxc_regulator_enable ( struct lt9611uxc * lt9611uxc )
{
int ret ;
ret = regulator_enable ( lt9611uxc - > supplies [ 0 ] . consumer ) ;
if ( ret < 0 )
return ret ;
usleep_range ( 1000 , 10000 ) ; /* 50000 according to dtsi */
ret = regulator_enable ( lt9611uxc - > supplies [ 1 ] . consumer ) ;
if ( ret < 0 ) {
regulator_disable ( lt9611uxc - > supplies [ 0 ] . consumer ) ;
return ret ;
}
return 0 ;
}
static struct lt9611uxc_mode * lt9611uxc_find_mode ( const struct drm_display_mode * mode )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( lt9611uxc_modes ) ; i + + ) {
if ( lt9611uxc_modes [ i ] . hdisplay = = mode - > hdisplay & &
lt9611uxc_modes [ i ] . vdisplay = = mode - > vdisplay & &
lt9611uxc_modes [ i ] . vrefresh = = drm_mode_vrefresh ( mode ) ) {
return & lt9611uxc_modes [ i ] ;
}
}
return NULL ;
}
static struct mipi_dsi_device * lt9611uxc_attach_dsi ( struct lt9611uxc * lt9611uxc ,
struct device_node * dsi_node )
{
const struct mipi_dsi_device_info info = { " lt9611uxc " , 0 , NULL } ;
struct mipi_dsi_device * dsi ;
struct mipi_dsi_host * host ;
2021-10-25 17:15:24 +02:00
struct device * dev = lt9611uxc - > dev ;
2020-11-02 04:14:35 +03:00
int ret ;
host = of_find_mipi_dsi_host_by_node ( dsi_node ) ;
if ( ! host ) {
2021-10-25 17:15:24 +02:00
dev_err ( dev , " failed to find dsi host \n " ) ;
2020-11-02 04:14:35 +03:00
return ERR_PTR ( - EPROBE_DEFER ) ;
}
2021-10-25 17:15:24 +02:00
dsi = devm_mipi_dsi_device_register_full ( dev , host , & info ) ;
2020-11-02 04:14:35 +03:00
if ( IS_ERR ( dsi ) ) {
2021-10-25 17:15:24 +02:00
dev_err ( dev , " failed to create dsi device \n " ) ;
2020-11-02 04:14:35 +03:00
return dsi ;
}
dsi - > lanes = 4 ;
dsi - > format = MIPI_DSI_FMT_RGB888 ;
dsi - > mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
MIPI_DSI_MODE_VIDEO_HSE ;
2021-10-25 17:15:24 +02:00
ret = devm_mipi_dsi_attach ( dev , dsi ) ;
2020-11-02 04:14:35 +03:00
if ( ret < 0 ) {
2021-10-25 17:15:24 +02:00
dev_err ( dev , " failed to attach dsi to host \n " ) ;
2020-11-02 04:14:35 +03:00
return ERR_PTR ( ret ) ;
}
return dsi ;
}
static int lt9611uxc_connector_get_modes ( struct drm_connector * connector )
{
struct lt9611uxc * lt9611uxc = connector_to_lt9611uxc ( connector ) ;
unsigned int count ;
struct edid * edid ;
edid = lt9611uxc - > bridge . funcs - > get_edid ( & lt9611uxc - > bridge , connector ) ;
drm_connector_update_edid_property ( connector , edid ) ;
count = drm_add_edid_modes ( connector , edid ) ;
kfree ( edid ) ;
return count ;
}
static enum drm_connector_status lt9611uxc_connector_detect ( struct drm_connector * connector ,
bool force )
{
struct lt9611uxc * lt9611uxc = connector_to_lt9611uxc ( connector ) ;
return lt9611uxc - > bridge . funcs - > detect ( & lt9611uxc - > bridge ) ;
}
static enum drm_mode_status lt9611uxc_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct lt9611uxc_mode * lt9611uxc_mode = lt9611uxc_find_mode ( mode ) ;
return lt9611uxc_mode ? MODE_OK : MODE_BAD ;
}
static const struct drm_connector_helper_funcs lt9611uxc_bridge_connector_helper_funcs = {
. get_modes = lt9611uxc_connector_get_modes ,
. mode_valid = lt9611uxc_connector_mode_valid ,
} ;
static const struct drm_connector_funcs lt9611uxc_bridge_connector_funcs = {
. fill_modes = drm_helper_probe_single_connector_modes ,
. detect = lt9611uxc_connector_detect ,
. destroy = drm_connector_cleanup ,
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
} ;
static int lt9611uxc_connector_init ( struct drm_bridge * bridge , struct lt9611uxc * lt9611uxc )
{
int ret ;
if ( ! bridge - > encoder ) {
DRM_ERROR ( " Parent encoder object not found " ) ;
return - ENODEV ;
}
2021-07-09 02:03:29 +03:00
lt9611uxc - > connector . polled = DRM_CONNECTOR_POLL_HPD ;
2020-11-02 04:14:35 +03:00
drm_connector_helper_add ( & lt9611uxc - > connector ,
& lt9611uxc_bridge_connector_helper_funcs ) ;
ret = drm_connector_init ( bridge - > dev , & lt9611uxc - > connector ,
& lt9611uxc_bridge_connector_funcs ,
DRM_MODE_CONNECTOR_HDMIA ) ;
if ( ret ) {
DRM_ERROR ( " Failed to initialize connector with drm \n " ) ;
return ret ;
}
return drm_connector_attach_encoder ( & lt9611uxc - > connector , bridge - > encoder ) ;
}
static int lt9611uxc_bridge_attach ( struct drm_bridge * bridge ,
enum drm_bridge_attach_flags flags )
{
struct lt9611uxc * lt9611uxc = bridge_to_lt9611uxc ( bridge ) ;
int ret ;
if ( ! ( flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ) ) {
ret = lt9611uxc_connector_init ( bridge , lt9611uxc ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
static enum drm_mode_status
lt9611uxc_bridge_mode_valid ( struct drm_bridge * bridge ,
const struct drm_display_info * info ,
const struct drm_display_mode * mode )
{
struct lt9611uxc_mode * lt9611uxc_mode ;
lt9611uxc_mode = lt9611uxc_find_mode ( mode ) ;
return lt9611uxc_mode ? MODE_OK : MODE_BAD ;
}
static void lt9611uxc_video_setup ( struct lt9611uxc * lt9611uxc ,
const struct drm_display_mode * mode )
{
u32 h_total , hactive , hsync_len , hfront_porch ;
u32 v_total , vactive , vsync_len , vfront_porch ;
h_total = mode - > htotal ;
v_total = mode - > vtotal ;
hactive = mode - > hdisplay ;
hsync_len = mode - > hsync_end - mode - > hsync_start ;
hfront_porch = mode - > hsync_start - mode - > hdisplay ;
vactive = mode - > vdisplay ;
vsync_len = mode - > vsync_end - mode - > vsync_start ;
vfront_porch = mode - > vsync_start - mode - > vdisplay ;
regmap_write ( lt9611uxc - > regmap , 0xd00d , ( u8 ) ( v_total / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd00e , ( u8 ) ( v_total % 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd00f , ( u8 ) ( vactive / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd010 , ( u8 ) ( vactive % 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd011 , ( u8 ) ( h_total / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd012 , ( u8 ) ( h_total % 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd013 , ( u8 ) ( hactive / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd014 , ( u8 ) ( hactive % 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd015 , ( u8 ) ( vsync_len % 256 ) ) ;
regmap_update_bits ( lt9611uxc - > regmap , 0xd016 , 0xf , ( u8 ) ( hsync_len / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd017 , ( u8 ) ( hsync_len % 256 ) ) ;
regmap_update_bits ( lt9611uxc - > regmap , 0xd018 , 0xf , ( u8 ) ( vfront_porch / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd019 , ( u8 ) ( vfront_porch % 256 ) ) ;
regmap_update_bits ( lt9611uxc - > regmap , 0xd01a , 0xf , ( u8 ) ( hfront_porch / 256 ) ) ;
regmap_write ( lt9611uxc - > regmap , 0xd01b , ( u8 ) ( hfront_porch % 256 ) ) ;
}
static void lt9611uxc_bridge_mode_set ( struct drm_bridge * bridge ,
const struct drm_display_mode * mode ,
const struct drm_display_mode * adj_mode )
{
struct lt9611uxc * lt9611uxc = bridge_to_lt9611uxc ( bridge ) ;
lt9611uxc_lock ( lt9611uxc ) ;
lt9611uxc_video_setup ( lt9611uxc , mode ) ;
lt9611uxc_unlock ( lt9611uxc ) ;
}
static enum drm_connector_status lt9611uxc_bridge_detect ( struct drm_bridge * bridge )
{
struct lt9611uxc * lt9611uxc = bridge_to_lt9611uxc ( bridge ) ;
unsigned int reg_val = 0 ;
int ret ;
2021-01-22 02:33:03 +03:00
bool connected = true ;
lt9611uxc_lock ( lt9611uxc ) ;
2020-11-02 04:14:35 +03:00
if ( lt9611uxc - > hpd_supported ) {
ret = regmap_read ( lt9611uxc - > regmap , 0xb023 , & reg_val ) ;
if ( ret )
dev_err ( lt9611uxc - > dev , " failed to read hpd status: %d \n " , ret ) ;
else
connected = reg_val & BIT ( 1 ) ;
}
2021-01-22 02:33:03 +03:00
lt9611uxc - > hdmi_connected = connected ;
lt9611uxc_unlock ( lt9611uxc ) ;
2020-11-02 04:14:35 +03:00
return connected ? connector_status_connected :
connector_status_disconnected ;
}
static int lt9611uxc_wait_for_edid ( struct lt9611uxc * lt9611uxc )
{
return wait_event_interruptible_timeout ( lt9611uxc - > wq , lt9611uxc - > edid_read ,
2021-01-22 02:33:01 +03:00
msecs_to_jiffies ( 500 ) ) ;
2020-11-02 04:14:35 +03:00
}
static int lt9611uxc_get_edid_block ( void * data , u8 * buf , unsigned int block , size_t len )
{
struct lt9611uxc * lt9611uxc = data ;
int ret ;
if ( len > EDID_BLOCK_SIZE )
return - EINVAL ;
if ( block > = EDID_NUM_BLOCKS )
return - EINVAL ;
lt9611uxc_lock ( lt9611uxc ) ;
regmap_write ( lt9611uxc - > regmap , 0xb00b , 0x10 ) ;
regmap_write ( lt9611uxc - > regmap , 0xb00a , block * EDID_BLOCK_SIZE ) ;
ret = regmap_noinc_read ( lt9611uxc - > regmap , 0xb0b0 , buf , len ) ;
if ( ret )
dev_err ( lt9611uxc - > dev , " edid read failed: %d \n " , ret ) ;
lt9611uxc_unlock ( lt9611uxc ) ;
return 0 ;
} ;
static struct edid * lt9611uxc_bridge_get_edid ( struct drm_bridge * bridge ,
struct drm_connector * connector )
{
struct lt9611uxc * lt9611uxc = bridge_to_lt9611uxc ( bridge ) ;
int ret ;
ret = lt9611uxc_wait_for_edid ( lt9611uxc ) ;
if ( ret < 0 ) {
dev_err ( lt9611uxc - > dev , " wait for EDID failed: %d \n " , ret ) ;
2021-01-22 02:33:02 +03:00
return NULL ;
} else if ( ret = = 0 ) {
dev_err ( lt9611uxc - > dev , " wait for EDID timeout \n " ) ;
return NULL ;
2020-11-02 04:14:35 +03:00
}
return drm_do_get_edid ( connector , lt9611uxc_get_edid_block , lt9611uxc ) ;
}
static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = {
. attach = lt9611uxc_bridge_attach ,
. mode_valid = lt9611uxc_bridge_mode_valid ,
. mode_set = lt9611uxc_bridge_mode_set ,
. detect = lt9611uxc_bridge_detect ,
. get_edid = lt9611uxc_bridge_get_edid ,
} ;
static int lt9611uxc_parse_dt ( struct device * dev ,
struct lt9611uxc * lt9611uxc )
{
lt9611uxc - > dsi0_node = of_graph_get_remote_node ( dev - > of_node , 0 , - 1 ) ;
if ( ! lt9611uxc - > dsi0_node ) {
dev_err ( lt9611uxc - > dev , " failed to get remote node for primary dsi \n " ) ;
return - ENODEV ;
}
lt9611uxc - > dsi1_node = of_graph_get_remote_node ( dev - > of_node , 1 , - 1 ) ;
return 0 ;
}
static int lt9611uxc_gpio_init ( struct lt9611uxc * lt9611uxc )
{
struct device * dev = lt9611uxc - > dev ;
lt9611uxc - > reset_gpio = devm_gpiod_get ( dev , " reset " , GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( lt9611uxc - > reset_gpio ) ) {
dev_err ( dev , " failed to acquire reset gpio \n " ) ;
return PTR_ERR ( lt9611uxc - > reset_gpio ) ;
}
lt9611uxc - > enable_gpio = devm_gpiod_get_optional ( dev , " enable " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( lt9611uxc - > enable_gpio ) ) {
dev_err ( dev , " failed to acquire enable gpio \n " ) ;
return PTR_ERR ( lt9611uxc - > enable_gpio ) ;
}
return 0 ;
}
static int lt9611uxc_read_device_rev ( struct lt9611uxc * lt9611uxc )
{
unsigned int rev0 , rev1 , rev2 ;
int ret ;
lt9611uxc_lock ( lt9611uxc ) ;
ret = regmap_read ( lt9611uxc - > regmap , 0x8100 , & rev0 ) ;
ret | = regmap_read ( lt9611uxc - > regmap , 0x8101 , & rev1 ) ;
ret | = regmap_read ( lt9611uxc - > regmap , 0x8102 , & rev2 ) ;
if ( ret )
dev_err ( lt9611uxc - > dev , " failed to read revision: %d \n " , ret ) ;
else
dev_info ( lt9611uxc - > dev , " LT9611 revision: 0x%02x.%02x.%02x \n " , rev0 , rev1 , rev2 ) ;
lt9611uxc_unlock ( lt9611uxc ) ;
return ret ;
}
static int lt9611uxc_read_version ( struct lt9611uxc * lt9611uxc )
{
unsigned int rev ;
int ret ;
lt9611uxc_lock ( lt9611uxc ) ;
ret = regmap_read ( lt9611uxc - > regmap , 0xb021 , & rev ) ;
if ( ret )
dev_err ( lt9611uxc - > dev , " failed to read revision: %d \n " , ret ) ;
else
dev_info ( lt9611uxc - > dev , " LT9611 version: 0x%02x \n " , rev ) ;
lt9611uxc_unlock ( lt9611uxc ) ;
return ret < 0 ? ret : rev ;
}
static int lt9611uxc_hdmi_hw_params ( struct device * dev , void * data ,
struct hdmi_codec_daifmt * fmt ,
struct hdmi_codec_params * hparms )
{
/*
* LT9611UXC will automatically detect rate and sample size , so no need
* to setup anything here .
*/
return 0 ;
}
static void lt9611uxc_audio_shutdown ( struct device * dev , void * data )
{
}
static int lt9611uxc_hdmi_i2s_get_dai_id ( struct snd_soc_component * component ,
struct device_node * endpoint )
{
struct of_endpoint of_ep ;
int ret ;
ret = of_graph_parse_endpoint ( endpoint , & of_ep ) ;
if ( ret < 0 )
return ret ;
/*
* HDMI sound should be located as reg = < 2 >
* Then , it is sound port 0
*/
if ( of_ep . port = = 2 )
return 0 ;
return - EINVAL ;
}
static const struct hdmi_codec_ops lt9611uxc_codec_ops = {
. hw_params = lt9611uxc_hdmi_hw_params ,
. audio_shutdown = lt9611uxc_audio_shutdown ,
. get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id ,
} ;
static int lt9611uxc_audio_init ( struct device * dev , struct lt9611uxc * lt9611uxc )
{
struct hdmi_codec_pdata codec_data = {
. ops = & lt9611uxc_codec_ops ,
. max_i2s_channels = 2 ,
. i2s = 1 ,
. data = lt9611uxc ,
} ;
lt9611uxc - > audio_pdev =
platform_device_register_data ( dev , HDMI_CODEC_DRV_NAME ,
PLATFORM_DEVID_AUTO ,
& codec_data , sizeof ( codec_data ) ) ;
return PTR_ERR_OR_ZERO ( lt9611uxc - > audio_pdev ) ;
}
static void lt9611uxc_audio_exit ( struct lt9611uxc * lt9611uxc )
{
if ( lt9611uxc - > audio_pdev ) {
platform_device_unregister ( lt9611uxc - > audio_pdev ) ;
lt9611uxc - > audio_pdev = NULL ;
}
}
# define LT9611UXC_FW_PAGE_SIZE 32
static void lt9611uxc_firmware_write_page ( struct lt9611uxc * lt9611uxc , u16 addr , const u8 * buf )
{
struct reg_sequence seq_write_prepare [ ] = {
REG_SEQ0 ( 0x805a , 0x04 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
REG_SEQ0 ( 0x805e , 0xdf ) ,
REG_SEQ0 ( 0x805a , 0x20 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
REG_SEQ0 ( 0x8058 , 0x21 ) ,
} ;
struct reg_sequence seq_write_addr [ ] = {
REG_SEQ0 ( 0x805b , ( addr > > 16 ) & 0xff ) ,
REG_SEQ0 ( 0x805c , ( addr > > 8 ) & 0xff ) ,
REG_SEQ0 ( 0x805d , addr & 0xff ) ,
REG_SEQ0 ( 0x805a , 0x10 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
} ;
regmap_write ( lt9611uxc - > regmap , 0x8108 , 0xbf ) ;
msleep ( 20 ) ;
regmap_write ( lt9611uxc - > regmap , 0x8108 , 0xff ) ;
msleep ( 20 ) ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_write_prepare , ARRAY_SIZE ( seq_write_prepare ) ) ;
regmap_noinc_write ( lt9611uxc - > regmap , 0x8059 , buf , LT9611UXC_FW_PAGE_SIZE ) ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_write_addr , ARRAY_SIZE ( seq_write_addr ) ) ;
msleep ( 20 ) ;
}
static void lt9611uxc_firmware_read_page ( struct lt9611uxc * lt9611uxc , u16 addr , char * buf )
{
struct reg_sequence seq_read_page [ ] = {
REG_SEQ0 ( 0x805a , 0xa0 ) ,
REG_SEQ0 ( 0x805a , 0x80 ) ,
REG_SEQ0 ( 0x805b , ( addr > > 16 ) & 0xff ) ,
REG_SEQ0 ( 0x805c , ( addr > > 8 ) & 0xff ) ,
REG_SEQ0 ( 0x805d , addr & 0xff ) ,
REG_SEQ0 ( 0x805a , 0x90 ) ,
REG_SEQ0 ( 0x805a , 0x80 ) ,
REG_SEQ0 ( 0x8058 , 0x21 ) ,
} ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_read_page , ARRAY_SIZE ( seq_read_page ) ) ;
regmap_noinc_read ( lt9611uxc - > regmap , 0x805f , buf , LT9611UXC_FW_PAGE_SIZE ) ;
}
static char * lt9611uxc_firmware_read ( struct lt9611uxc * lt9611uxc , size_t size )
{
struct reg_sequence seq_read_setup [ ] = {
REG_SEQ0 ( 0x805a , 0x84 ) ,
REG_SEQ0 ( 0x805a , 0x80 ) ,
} ;
char * readbuf ;
u16 offset ;
readbuf = kzalloc ( ALIGN ( size , 32 ) , GFP_KERNEL ) ;
if ( ! readbuf )
return NULL ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_read_setup , ARRAY_SIZE ( seq_read_setup ) ) ;
for ( offset = 0 ;
offset < size ;
offset + = LT9611UXC_FW_PAGE_SIZE )
lt9611uxc_firmware_read_page ( lt9611uxc , offset , & readbuf [ offset ] ) ;
return readbuf ;
}
static int lt9611uxc_firmware_update ( struct lt9611uxc * lt9611uxc )
{
int ret ;
u16 offset ;
size_t remain ;
char * readbuf ;
const struct firmware * fw ;
struct reg_sequence seq_setup [ ] = {
REG_SEQ0 ( 0x805e , 0xdf ) ,
REG_SEQ0 ( 0x8058 , 0x00 ) ,
REG_SEQ0 ( 0x8059 , 0x50 ) ,
REG_SEQ0 ( 0x805a , 0x10 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
} ;
struct reg_sequence seq_block_erase [ ] = {
REG_SEQ0 ( 0x805a , 0x04 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
REG_SEQ0 ( 0x805b , 0x00 ) ,
REG_SEQ0 ( 0x805c , 0x00 ) ,
REG_SEQ0 ( 0x805d , 0x00 ) ,
REG_SEQ0 ( 0x805a , 0x01 ) ,
REG_SEQ0 ( 0x805a , 0x00 ) ,
} ;
ret = request_firmware ( & fw , " lt9611uxc_fw.bin " , lt9611uxc - > dev ) ;
if ( ret < 0 )
return ret ;
dev_info ( lt9611uxc - > dev , " Updating firmware \n " ) ;
lt9611uxc_lock ( lt9611uxc ) ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_setup , ARRAY_SIZE ( seq_setup ) ) ;
/*
* Need erase block 2 timess here . Sometimes , block erase can fail .
* This is a workaroud .
*/
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_block_erase , ARRAY_SIZE ( seq_block_erase ) ) ;
msleep ( 3000 ) ;
regmap_multi_reg_write ( lt9611uxc - > regmap , seq_block_erase , ARRAY_SIZE ( seq_block_erase ) ) ;
msleep ( 3000 ) ;
for ( offset = 0 , remain = fw - > size ;
remain > = LT9611UXC_FW_PAGE_SIZE ;
offset + = LT9611UXC_FW_PAGE_SIZE , remain - = LT9611UXC_FW_PAGE_SIZE )
lt9611uxc_firmware_write_page ( lt9611uxc , offset , fw - > data + offset ) ;
if ( remain > 0 ) {
char buf [ LT9611UXC_FW_PAGE_SIZE ] ;
memset ( buf , 0xff , LT9611UXC_FW_PAGE_SIZE ) ;
memcpy ( buf , fw - > data + offset , remain ) ;
lt9611uxc_firmware_write_page ( lt9611uxc , offset , buf ) ;
}
msleep ( 20 ) ;
readbuf = lt9611uxc_firmware_read ( lt9611uxc , fw - > size ) ;
if ( ! readbuf ) {
ret = - ENOMEM ;
goto out ;
}
if ( ! memcmp ( readbuf , fw - > data , fw - > size ) ) {
dev_err ( lt9611uxc - > dev , " Firmware update failed \n " ) ;
print_hex_dump ( KERN_ERR , " fw: " , DUMP_PREFIX_OFFSET , 16 , 1 , readbuf , fw - > size , false ) ;
ret = - EINVAL ;
} else {
dev_info ( lt9611uxc - > dev , " Firmware updates successfully \n " ) ;
ret = 0 ;
}
kfree ( readbuf ) ;
out :
lt9611uxc_unlock ( lt9611uxc ) ;
lt9611uxc_reset ( lt9611uxc ) ;
release_firmware ( fw ) ;
return ret ;
}
static ssize_t lt9611uxc_firmware_store ( struct device * dev , struct device_attribute * attr , const char * buf , size_t len )
{
struct lt9611uxc * lt9611uxc = dev_get_drvdata ( dev ) ;
int ret ;
ret = lt9611uxc_firmware_update ( lt9611uxc ) ;
if ( ret < 0 )
return ret ;
return len ;
}
static ssize_t lt9611uxc_firmware_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct lt9611uxc * lt9611uxc = dev_get_drvdata ( dev ) ;
2021-02-07 17:10:00 +08:00
return sysfs_emit ( buf , " %02x \n " , lt9611uxc - > fw_version ) ;
2020-11-02 04:14:35 +03:00
}
static DEVICE_ATTR_RW ( lt9611uxc_firmware ) ;
static struct attribute * lt9611uxc_attrs [ ] = {
& dev_attr_lt9611uxc_firmware . attr ,
NULL ,
} ;
static const struct attribute_group lt9611uxc_attr_group = {
. attrs = lt9611uxc_attrs ,
} ;
static const struct attribute_group * lt9611uxc_attr_groups [ ] = {
& lt9611uxc_attr_group ,
NULL ,
} ;
static int lt9611uxc_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct lt9611uxc * lt9611uxc ;
struct device * dev = & client - > dev ;
int ret ;
bool fw_updated = false ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) ) {
dev_err ( dev , " device doesn't support I2C \n " ) ;
return - ENODEV ;
}
lt9611uxc = devm_kzalloc ( dev , sizeof ( * lt9611uxc ) , GFP_KERNEL ) ;
if ( ! lt9611uxc )
return - ENOMEM ;
2022-01-17 23:41:50 +08:00
lt9611uxc - > dev = dev ;
2020-11-02 04:14:35 +03:00
lt9611uxc - > client = client ;
mutex_init ( & lt9611uxc - > ocm_lock ) ;
lt9611uxc - > regmap = devm_regmap_init_i2c ( client , & lt9611uxc_regmap_config ) ;
if ( IS_ERR ( lt9611uxc - > regmap ) ) {
dev_err ( lt9611uxc - > dev , " regmap i2c init failed \n " ) ;
return PTR_ERR ( lt9611uxc - > regmap ) ;
}
2022-01-17 23:41:50 +08:00
ret = lt9611uxc_parse_dt ( dev , lt9611uxc ) ;
2020-11-02 04:14:35 +03:00
if ( ret ) {
dev_err ( dev , " failed to parse device tree \n " ) ;
return ret ;
}
ret = lt9611uxc_gpio_init ( lt9611uxc ) ;
if ( ret < 0 )
goto err_of_put ;
ret = lt9611uxc_regulator_init ( lt9611uxc ) ;
if ( ret < 0 )
goto err_of_put ;
lt9611uxc_assert_5v ( lt9611uxc ) ;
ret = lt9611uxc_regulator_enable ( lt9611uxc ) ;
if ( ret )
goto err_of_put ;
lt9611uxc_reset ( lt9611uxc ) ;
ret = lt9611uxc_read_device_rev ( lt9611uxc ) ;
if ( ret ) {
dev_err ( dev , " failed to read chip rev \n " ) ;
goto err_disable_regulators ;
}
retry :
ret = lt9611uxc_read_version ( lt9611uxc ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to read FW version \n " ) ;
goto err_disable_regulators ;
} else if ( ret = = 0 ) {
if ( ! fw_updated ) {
fw_updated = true ;
dev_err ( dev , " FW version 0, enforcing firmware update \n " ) ;
ret = lt9611uxc_firmware_update ( lt9611uxc ) ;
if ( ret < 0 )
goto err_disable_regulators ;
else
goto retry ;
} else {
dev_err ( dev , " FW version 0, update failed \n " ) ;
ret = - EOPNOTSUPP ;
goto err_disable_regulators ;
}
} else if ( ret < 0x40 ) {
dev_info ( dev , " FW version 0x%x, HPD not supported \n " , ret ) ;
} else {
lt9611uxc - > hpd_supported = true ;
}
lt9611uxc - > fw_version = ret ;
init_waitqueue_head ( & lt9611uxc - > wq ) ;
2021-01-22 02:33:03 +03:00
INIT_WORK ( & lt9611uxc - > work , lt9611uxc_hpd_work ) ;
2020-11-02 04:14:35 +03:00
ret = devm_request_threaded_irq ( dev , client - > irq , NULL ,
lt9611uxc_irq_thread_handler ,
IRQF_ONESHOT , " lt9611uxc " , lt9611uxc ) ;
if ( ret ) {
dev_err ( dev , " failed to request irq \n " ) ;
goto err_disable_regulators ;
}
i2c_set_clientdata ( client , lt9611uxc ) ;
lt9611uxc - > bridge . funcs = & lt9611uxc_bridge_funcs ;
lt9611uxc - > bridge . of_node = client - > dev . of_node ;
lt9611uxc - > bridge . ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID ;
if ( lt9611uxc - > hpd_supported )
lt9611uxc - > bridge . ops | = DRM_BRIDGE_OP_HPD ;
lt9611uxc - > bridge . type = DRM_MODE_CONNECTOR_HDMIA ;
drm_bridge_add ( & lt9611uxc - > bridge ) ;
2021-10-25 17:15:25 +02:00
/* Attach primary DSI */
lt9611uxc - > dsi0 = lt9611uxc_attach_dsi ( lt9611uxc , lt9611uxc - > dsi0_node ) ;
if ( IS_ERR ( lt9611uxc - > dsi0 ) ) {
ret = PTR_ERR ( lt9611uxc - > dsi0 ) ;
goto err_remove_bridge ;
}
/* Attach secondary DSI, if specified */
if ( lt9611uxc - > dsi1_node ) {
lt9611uxc - > dsi1 = lt9611uxc_attach_dsi ( lt9611uxc , lt9611uxc - > dsi1_node ) ;
if ( IS_ERR ( lt9611uxc - > dsi1 ) ) {
ret = PTR_ERR ( lt9611uxc - > dsi1 ) ;
goto err_remove_bridge ;
}
}
2020-11-02 04:14:35 +03:00
return lt9611uxc_audio_init ( dev , lt9611uxc ) ;
2021-10-25 17:15:25 +02:00
err_remove_bridge :
drm_bridge_remove ( & lt9611uxc - > bridge ) ;
2020-11-02 04:14:35 +03:00
err_disable_regulators :
regulator_bulk_disable ( ARRAY_SIZE ( lt9611uxc - > supplies ) , lt9611uxc - > supplies ) ;
err_of_put :
of_node_put ( lt9611uxc - > dsi1_node ) ;
of_node_put ( lt9611uxc - > dsi0_node ) ;
return ret ;
}
static int lt9611uxc_remove ( struct i2c_client * client )
{
struct lt9611uxc * lt9611uxc = i2c_get_clientdata ( client ) ;
disable_irq ( client - > irq ) ;
2021-01-22 02:33:03 +03:00
flush_scheduled_work ( ) ;
2020-11-02 04:14:35 +03:00
lt9611uxc_audio_exit ( lt9611uxc ) ;
drm_bridge_remove ( & lt9611uxc - > bridge ) ;
mutex_destroy ( & lt9611uxc - > ocm_lock ) ;
regulator_bulk_disable ( ARRAY_SIZE ( lt9611uxc - > supplies ) , lt9611uxc - > supplies ) ;
of_node_put ( lt9611uxc - > dsi1_node ) ;
of_node_put ( lt9611uxc - > dsi0_node ) ;
return 0 ;
}
static struct i2c_device_id lt9611uxc_id [ ] = {
{ " lontium,lt9611uxc " , 0 } ,
{ /* sentinel */ }
} ;
static const struct of_device_id lt9611uxc_match_table [ ] = {
{ . compatible = " lontium,lt9611uxc " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , lt9611uxc_match_table ) ;
static struct i2c_driver lt9611uxc_driver = {
. driver = {
. name = " lt9611uxc " ,
. of_match_table = lt9611uxc_match_table ,
. dev_groups = lt9611uxc_attr_groups ,
} ,
. probe = lt9611uxc_probe ,
. remove = lt9611uxc_remove ,
. id_table = lt9611uxc_id ,
} ;
module_i2c_driver ( lt9611uxc_driver ) ;
MODULE_AUTHOR ( " Dmitry Baryshkov <dmitry.baryshkov@linaro.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;