2018-02-07 17:34:26 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-07-18 12:45:16 +04:00
/*
* adv7604 - Analog Devices ADV7604 video decoder driver
*
* Copyright 2012 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
*
*/
/*
* References ( c = chapter , p = page ) :
* REF_01 - Analog devices , ADV7604 , Register Settings Recommendations ,
* Revision 2.5 , June 2010
* REF_02 - Analog devices , Register map documentation , Documentation of
* the register maps , Software manual , Rev . F , June 2010
* REF_03 - Analog devices , ADV7604 , Hardware Manual , Rev . F , August 2010
*/
2014-01-31 02:18:34 +04:00
# include <linux/delay.h>
2014-01-31 01:37:08 +04:00
# include <linux/gpio/consumer.h>
2015-06-07 13:32:33 +03:00
# include <linux/hdmi.h>
2014-01-31 02:18:34 +04:00
# include <linux/i2c.h>
2012-07-18 12:45:16 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2016-08-27 02:17:25 +03:00
# include <linux/of_graph.h>
2012-07-18 12:45:16 +04:00
# include <linux/slab.h>
2014-01-31 02:18:34 +04:00
# include <linux/v4l2-dv-timings.h>
2012-07-18 12:45:16 +04:00
# include <linux/videodev2.h>
# include <linux/workqueue.h>
2015-06-19 16:23:06 +03:00
# include <linux/regmap.h>
2018-11-27 01:01:09 +03:00
# include <linux/interrupt.h>
2014-01-31 02:18:34 +04:00
2015-11-10 17:01:44 +03:00
# include <media/i2c/adv7604.h>
2015-09-07 14:12:57 +03:00
# include <media/cec.h>
2012-07-18 12:45:16 +04:00
# include <media/v4l2-ctrls.h>
2014-01-31 02:18:34 +04:00
# include <media/v4l2-device.h>
2015-06-24 19:50:27 +03:00
# include <media/v4l2-event.h>
2013-07-29 15:40:56 +04:00
# include <media/v4l2-dv-timings.h>
2016-08-27 02:17:25 +03:00
# include <media/v4l2-fwnode.h>
2012-07-18 12:45:16 +04:00
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " debug level (0-2) " ) ;
MODULE_DESCRIPTION ( " Analog Devices ADV7604 video decoder driver " ) ;
MODULE_AUTHOR ( " Hans Verkuil <hans.verkuil@cisco.com> " ) ;
MODULE_AUTHOR ( " Mats Randgaard <mats.randgaard@cisco.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* ADV7604 system clock frequency */
2015-02-03 20:13:18 +03:00
# define ADV76XX_FSC (28636360)
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
# define ADV76XX_RGB_OUT (1 << 1)
2014-01-27 01:42:37 +04:00
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_FORMAT_SEL_8BIT (0 << 0)
2014-01-27 01:42:37 +04:00
# define ADV7604_OP_FORMAT_SEL_10BIT (1 << 0)
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_FORMAT_SEL_12BIT (2 << 0)
2014-01-27 01:42:37 +04:00
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_MODE_SEL_SDR_422 (0 << 5)
2014-01-27 01:42:37 +04:00
# define ADV7604_OP_MODE_SEL_DDR_422 (1 << 5)
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_MODE_SEL_SDR_444 (2 << 5)
2014-01-27 01:42:37 +04:00
# define ADV7604_OP_MODE_SEL_DDR_444 (3 << 5)
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_MODE_SEL_SDR_422_2X (4 << 5)
2014-01-27 01:42:37 +04:00
# define ADV7604_OP_MODE_SEL_ADI_CM (5 << 5)
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_CH_SEL_GBR (0 << 5)
# define ADV76XX_OP_CH_SEL_GRB (1 << 5)
# define ADV76XX_OP_CH_SEL_BGR (2 << 5)
# define ADV76XX_OP_CH_SEL_RGB (3 << 5)
# define ADV76XX_OP_CH_SEL_BRG (4 << 5)
# define ADV76XX_OP_CH_SEL_RBG (5 << 5)
2014-01-27 01:42:37 +04:00
2015-02-03 20:13:18 +03:00
# define ADV76XX_OP_SWAP_CB_CR (1 << 0)
2014-01-27 01:42:37 +04:00
2015-09-07 14:12:57 +03:00
# define ADV76XX_MAX_ADDRS (3)
2015-02-03 20:13:18 +03:00
enum adv76xx_type {
2013-11-25 22:45:07 +04:00
ADV7604 ,
ADV7611 ,
2015-06-03 16:59:51 +03:00
ADV7612 ,
2013-11-25 22:45:07 +04:00
} ;
2015-02-03 20:13:18 +03:00
struct adv76xx_reg_seq {
2013-11-25 22:45:07 +04:00
unsigned int reg ;
u8 val ;
} ;
2015-02-03 20:13:18 +03:00
struct adv76xx_format_info {
2014-11-10 20:28:29 +03:00
u32 code ;
2014-01-27 01:42:37 +04:00
u8 op_ch_sel ;
bool rgb_out ;
bool swap_cb_cr ;
u8 op_format_sel ;
} ;
2015-06-07 13:32:33 +03:00
struct adv76xx_cfg_read_infoframe {
const char * desc ;
u8 present_mask ;
u8 head_addr ;
u8 payload_addr ;
} ;
2015-02-03 20:13:18 +03:00
struct adv76xx_chip_info {
enum adv76xx_type type ;
2013-11-25 22:45:07 +04:00
bool has_afe ;
unsigned int max_port ;
unsigned int num_dv_ports ;
unsigned int edid_enable_reg ;
unsigned int edid_status_reg ;
unsigned int lcf_reg ;
unsigned int cable_det_mask ;
unsigned int tdms_lock_mask ;
unsigned int fmt_change_digital_mask ;
2015-02-04 17:16:00 +03:00
unsigned int cp_csc ;
2013-11-25 22:45:07 +04:00
2018-10-12 14:30:02 +03:00
unsigned int cec_irq_status ;
unsigned int cec_rx_enable ;
unsigned int cec_rx_enable_mask ;
bool cec_irq_swap ;
2015-02-03 20:13:18 +03:00
const struct adv76xx_format_info * formats ;
2014-01-27 01:42:37 +04:00
unsigned int nformats ;
2013-11-25 22:45:07 +04:00
void ( * set_termination ) ( struct v4l2_subdev * sd , bool enable ) ;
void ( * setup_irqs ) ( struct v4l2_subdev * sd ) ;
unsigned int ( * read_hdmi_pixelclock ) ( struct v4l2_subdev * sd ) ;
unsigned int ( * read_cable_det ) ( struct v4l2_subdev * sd ) ;
/* 0 = AFE, 1 = HDMI */
2015-02-03 20:13:18 +03:00
const struct adv76xx_reg_seq * recommended_settings [ 2 ] ;
2013-11-25 22:45:07 +04:00
unsigned int num_recommended_settings [ 2 ] ;
unsigned long page_mask ;
2015-04-09 11:25:46 +03:00
/* Masks for timings */
unsigned int linewidth_mask ;
unsigned int field0_height_mask ;
unsigned int field1_height_mask ;
unsigned int hfrontporch_mask ;
unsigned int hsync_mask ;
unsigned int hbackporch_mask ;
unsigned int field0_vfrontporch_mask ;
unsigned int field1_vfrontporch_mask ;
unsigned int field0_vsync_mask ;
unsigned int field1_vsync_mask ;
unsigned int field0_vbackporch_mask ;
unsigned int field1_vbackporch_mask ;
2013-11-25 22:45:07 +04:00
} ;
2012-07-18 12:45:16 +04:00
/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Arrays with configuration parameters for the ADV7604
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
2014-01-29 17:08:58 +04:00
2015-02-03 20:13:18 +03:00
struct adv76xx_state {
const struct adv76xx_chip_info * info ;
struct adv76xx_platform_data pdata ;
2014-01-27 01:42:37 +04:00
2014-01-31 01:37:08 +04:00
struct gpio_desc * hpd_gpio [ 4 ] ;
2016-06-22 14:30:42 +03:00
struct gpio_desc * reset_gpio ;
2014-01-31 01:37:08 +04:00
2012-07-18 12:45:16 +04:00
struct v4l2_subdev sd ;
2015-02-03 20:13:18 +03:00
struct media_pad pads [ ADV76XX_PAD_MAX ] ;
2014-01-29 17:08:58 +04:00
unsigned int source_pad ;
2014-01-27 01:42:37 +04:00
2012-07-18 12:45:16 +04:00
struct v4l2_ctrl_handler hdl ;
2014-01-27 01:42:37 +04:00
2015-02-03 20:13:18 +03:00
enum adv76xx_pad selected_input ;
2014-01-27 01:42:37 +04:00
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings timings ;
2015-02-03 20:13:18 +03:00
const struct adv76xx_format_info * format ;
2014-01-27 01:42:37 +04:00
2013-12-10 16:45:00 +04:00
struct {
u8 edid [ 256 ] ;
u32 present ;
unsigned blocks ;
} edid ;
2013-12-10 16:57:09 +04:00
u16 spa_port_a [ 2 ] ;
2012-07-18 12:45:16 +04:00
struct v4l2_fract aspect_ratio ;
u32 rgb_quantization_range ;
struct delayed_work delayed_work_enable_hotplug ;
2012-10-16 17:12:55 +04:00
bool restart_stdi_once ;
2012-07-18 12:45:16 +04:00
2016-07-09 00:16:10 +03:00
/* CEC */
2015-09-07 14:12:57 +03:00
struct cec_adapter * cec_adap ;
u8 cec_addr [ ADV76XX_MAX_ADDRS ] ;
u8 cec_valid_addrs ;
bool cec_enabled_adap ;
2012-07-18 12:45:16 +04:00
/* i2c clients */
2015-02-03 20:13:18 +03:00
struct i2c_client * i2c_clients [ ADV76XX_PAGE_MAX ] ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
/* Regmaps */
struct regmap * regmap [ ADV76XX_PAGE_MAX ] ;
2012-07-18 12:45:16 +04:00
/* controls */
struct v4l2_ctrl * detect_tx_5v_ctrl ;
struct v4l2_ctrl * analog_sampling_phase_ctrl ;
struct v4l2_ctrl * free_run_color_manual_ctrl ;
struct v4l2_ctrl * free_run_color_ctrl ;
struct v4l2_ctrl * rgb_quantization_range_ctrl ;
} ;
2015-02-03 20:13:18 +03:00
static bool adv76xx_has_afe ( struct adv76xx_state * state )
2013-11-25 22:45:07 +04:00
{
return state - > info - > has_afe ;
}
2016-01-27 14:04:50 +03:00
/* Unsupported timings. This device cannot support 720p30. */
static const struct v4l2_dv_timings adv76xx_timings_exceptions [ ] = {
V4L2_DV_BT_CEA_1280X720P30 ,
{ }
2012-07-18 12:45:16 +04:00
} ;
2016-01-27 14:04:50 +03:00
static bool adv76xx_check_dv_timings ( const struct v4l2_dv_timings * t , void * hdl )
{
int i ;
for ( i = 0 ; adv76xx_timings_exceptions [ i ] . bt . width ; i + + )
if ( v4l2_match_dv_timings ( t , adv76xx_timings_exceptions + i , 0 , false ) )
return false ;
return true ;
}
2015-02-03 20:13:18 +03:00
struct adv76xx_video_standards {
2012-10-16 17:02:05 +04:00
struct v4l2_dv_timings timings ;
u8 vid_std ;
u8 v_freq ;
} ;
/* sorted by number of lines */
2015-02-03 20:13:18 +03:00
static const struct adv76xx_video_standards adv7604_prim_mode_comp [ ] = {
2012-10-16 17:02:05 +04:00
/* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */
{ V4L2_DV_BT_CEA_720X576P50 , 0x0b , 0x00 } ,
{ V4L2_DV_BT_CEA_1280X720P50 , 0x19 , 0x01 } ,
{ V4L2_DV_BT_CEA_1280X720P60 , 0x19 , 0x00 } ,
{ V4L2_DV_BT_CEA_1920X1080P24 , 0x1e , 0x04 } ,
{ V4L2_DV_BT_CEA_1920X1080P25 , 0x1e , 0x03 } ,
{ V4L2_DV_BT_CEA_1920X1080P30 , 0x1e , 0x02 } ,
{ V4L2_DV_BT_CEA_1920X1080P50 , 0x1e , 0x01 } ,
{ V4L2_DV_BT_CEA_1920X1080P60 , 0x1e , 0x00 } ,
/* TODO add 1920x1080P60_RB (CVT timing) */
{ } ,
} ;
/* sorted by number of lines */
2015-02-03 20:13:18 +03:00
static const struct adv76xx_video_standards adv7604_prim_mode_gr [ ] = {
2012-10-16 17:02:05 +04:00
{ V4L2_DV_BT_DMT_640X480P60 , 0x08 , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P72 , 0x09 , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P75 , 0x0a , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P85 , 0x0b , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P56 , 0x00 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P60 , 0x01 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P72 , 0x02 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P75 , 0x03 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P85 , 0x04 , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P60 , 0x0c , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P70 , 0x0d , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P75 , 0x0e , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P85 , 0x0f , 0x00 } ,
{ V4L2_DV_BT_DMT_1280X1024P60 , 0x05 , 0x00 } ,
{ V4L2_DV_BT_DMT_1280X1024P75 , 0x06 , 0x00 } ,
{ V4L2_DV_BT_DMT_1360X768P60 , 0x12 , 0x00 } ,
{ V4L2_DV_BT_DMT_1366X768P60 , 0x13 , 0x00 } ,
{ V4L2_DV_BT_DMT_1400X1050P60 , 0x14 , 0x00 } ,
{ V4L2_DV_BT_DMT_1400X1050P75 , 0x15 , 0x00 } ,
{ V4L2_DV_BT_DMT_1600X1200P60 , 0x16 , 0x00 } , /* TODO not tested */
/* TODO add 1600X1200P60_RB (not a DMT timing) */
{ V4L2_DV_BT_DMT_1680X1050P60 , 0x18 , 0x00 } ,
{ V4L2_DV_BT_DMT_1920X1200P60_RB , 0x19 , 0x00 } , /* TODO not tested */
{ } ,
} ;
/* sorted by number of lines */
2015-02-03 20:13:18 +03:00
static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_comp [ ] = {
2012-10-16 17:02:05 +04:00
{ V4L2_DV_BT_CEA_720X480P59_94 , 0x0a , 0x00 } ,
{ V4L2_DV_BT_CEA_720X576P50 , 0x0b , 0x00 } ,
{ V4L2_DV_BT_CEA_1280X720P50 , 0x13 , 0x01 } ,
{ V4L2_DV_BT_CEA_1280X720P60 , 0x13 , 0x00 } ,
{ V4L2_DV_BT_CEA_1920X1080P24 , 0x1e , 0x04 } ,
{ V4L2_DV_BT_CEA_1920X1080P25 , 0x1e , 0x03 } ,
{ V4L2_DV_BT_CEA_1920X1080P30 , 0x1e , 0x02 } ,
{ V4L2_DV_BT_CEA_1920X1080P50 , 0x1e , 0x01 } ,
{ V4L2_DV_BT_CEA_1920X1080P60 , 0x1e , 0x00 } ,
{ } ,
} ;
/* sorted by number of lines */
2015-02-03 20:13:18 +03:00
static const struct adv76xx_video_standards adv76xx_prim_mode_hdmi_gr [ ] = {
2012-10-16 17:02:05 +04:00
{ V4L2_DV_BT_DMT_640X480P60 , 0x08 , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P72 , 0x09 , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P75 , 0x0a , 0x00 } ,
{ V4L2_DV_BT_DMT_640X480P85 , 0x0b , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P56 , 0x00 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P60 , 0x01 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P72 , 0x02 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P75 , 0x03 , 0x00 } ,
{ V4L2_DV_BT_DMT_800X600P85 , 0x04 , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P60 , 0x0c , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P70 , 0x0d , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P75 , 0x0e , 0x00 } ,
{ V4L2_DV_BT_DMT_1024X768P85 , 0x0f , 0x00 } ,
{ V4L2_DV_BT_DMT_1280X1024P60 , 0x05 , 0x00 } ,
{ V4L2_DV_BT_DMT_1280X1024P75 , 0x06 , 0x00 } ,
{ } ,
} ;
2015-05-07 16:37:57 +03:00
static const struct v4l2_event adv76xx_ev_fmt = {
. type = V4L2_EVENT_SOURCE_CHANGE ,
. u . src_change . changes = V4L2_EVENT_SRC_CH_RESOLUTION ,
} ;
2012-07-18 12:45:16 +04:00
/* ----------------------------------------------------------------------- */
2015-02-03 20:13:18 +03:00
static inline struct adv76xx_state * to_state ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
return container_of ( sd , struct adv76xx_state , sd ) ;
2012-07-18 12:45:16 +04:00
}
static inline unsigned htotal ( const struct v4l2_bt_timings * t )
{
2013-07-29 15:40:59 +04:00
return V4L2_DV_BT_FRAME_WIDTH ( t ) ;
2012-07-18 12:45:16 +04:00
}
static inline unsigned vtotal ( const struct v4l2_bt_timings * t )
{
2013-07-29 15:40:59 +04:00
return V4L2_DV_BT_FRAME_HEIGHT ( t ) ;
2012-07-18 12:45:16 +04:00
}
/* ----------------------------------------------------------------------- */
2015-06-19 16:23:06 +03:00
static int adv76xx_read_check ( struct adv76xx_state * state ,
int client_page , u8 reg )
2012-07-18 12:45:16 +04:00
{
2015-06-19 16:23:06 +03:00
struct i2c_client * client = state - > i2c_clients [ client_page ] ;
2012-07-18 12:45:16 +04:00
int err ;
2015-06-19 16:23:06 +03:00
unsigned int val ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
err = regmap_read ( state - > regmap [ client_page ] , reg , & val ) ;
if ( err ) {
v4l_err ( client , " error reading %02x, %02x \n " ,
client - > addr , reg ) ;
return err ;
2012-07-18 12:45:16 +04:00
}
2015-06-19 16:23:06 +03:00
return val ;
2012-07-18 12:45:16 +04:00
}
2015-06-19 16:23:06 +03:00
/* adv76xx_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX
* size to one or more registers .
*
* A value of zero will be returned on success , a negative errno will
* be returned in error cases .
*/
static int adv76xx_write_block ( struct adv76xx_state * state , int client_page ,
unsigned int init_reg , const void * val ,
size_t val_len )
2012-07-18 12:45:16 +04:00
{
2015-06-19 16:23:06 +03:00
struct regmap * regmap = state - > regmap [ client_page ] ;
if ( val_len > I2C_SMBUS_BLOCK_MAX )
val_len = I2C_SMBUS_BLOCK_MAX ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_raw_write ( regmap , init_reg , val , val_len ) ;
2012-07-18 12:45:16 +04:00
}
/* ----------------------------------------------------------------------- */
static inline int io_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_IO , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int io_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_IO ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
2015-09-07 14:12:57 +03:00
static inline int io_write_clr_set ( struct v4l2_subdev * sd , u8 reg , u8 mask ,
u8 val )
2012-07-18 12:45:16 +04:00
{
2014-01-31 00:17:42 +04:00
return io_write ( sd , reg , ( io_read ( sd , reg ) & ~ mask ) | val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int avlink_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV7604_PAGE_AVLINK , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int avlink_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV7604_PAGE_AVLINK ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int cec_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_CEC , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int cec_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_CEC ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
2015-09-07 14:12:57 +03:00
static inline int cec_write_clr_set ( struct v4l2_subdev * sd , u8 reg , u8 mask ,
u8 val )
{
return cec_write ( sd , reg , ( cec_read ( sd , reg ) & ~ mask ) | val ) ;
}
2012-07-18 12:45:16 +04:00
static inline int infoframe_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_INFOFRAME , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int infoframe_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_INFOFRAME ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int afe_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_AFE , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int afe_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_AFE ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int rep_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_REP , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int rep_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_REP ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
2014-01-31 00:17:42 +04:00
static inline int rep_write_clr_set ( struct v4l2_subdev * sd , u8 reg , u8 mask , u8 val )
2012-07-18 12:45:16 +04:00
{
2014-01-31 00:17:42 +04:00
return rep_write ( sd , reg , ( rep_read ( sd , reg ) & ~ mask ) | val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int edid_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_EDID , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int edid_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_EDID ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int edid_write_block ( struct v4l2_subdev * sd ,
2015-06-19 16:23:06 +03:00
unsigned int total_len , const u8 * val )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
int err = 0 ;
2015-06-19 16:23:06 +03:00
int i = 0 ;
int len = 0 ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
v4l2_dbg ( 2 , debug , sd , " %s: write EDID block (%d byte) \n " ,
__func__ , total_len ) ;
while ( ! err & & i < total_len ) {
len = ( total_len - i ) > I2C_SMBUS_BLOCK_MAX ?
I2C_SMBUS_BLOCK_MAX :
( total_len - i ) ;
err = adv76xx_write_block ( state , ADV76XX_PAGE_EDID ,
i , val + i , len ) ;
i + = len ;
}
2012-07-18 12:45:16 +04:00
2013-12-10 16:57:09 +04:00
return err ;
}
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
static void adv76xx_set_hpd ( struct adv76xx_state * state , unsigned int hpd )
2014-01-31 01:37:08 +04:00
{
unsigned int i ;
2015-03-02 10:00:44 +03:00
for ( i = 0 ; i < state - > info - > num_dv_ports ; + + i )
2014-01-31 01:37:08 +04:00
gpiod_set_value_cansleep ( state - > hpd_gpio [ i ] , hpd & BIT ( i ) ) ;
2015-02-03 20:13:18 +03:00
v4l2_subdev_notify ( & state - > sd , ADV76XX_HOTPLUG , & hpd ) ;
2014-01-31 01:37:08 +04:00
}
2015-02-03 20:13:18 +03:00
static void adv76xx_delayed_work_enable_hotplug ( struct work_struct * work )
2013-12-10 16:57:09 +04:00
{
struct delayed_work * dwork = to_delayed_work ( work ) ;
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = container_of ( dwork , struct adv76xx_state ,
2013-12-10 16:57:09 +04:00
delayed_work_enable_hotplug ) ;
struct v4l2_subdev * sd = & state - > sd ;
2012-07-18 12:45:16 +04:00
2013-12-10 16:57:09 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: enable hotplug \n " , __func__ ) ;
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
adv76xx_set_hpd ( state , state - > edid . present ) ;
2012-07-18 12:45:16 +04:00
}
static inline int hdmi_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_HDMI , reg ) ;
2012-07-18 12:45:16 +04:00
}
2014-01-09 02:30:37 +04:00
static u16 hdmi_read16 ( struct v4l2_subdev * sd , u8 reg , u16 mask )
{
return ( ( hdmi_read ( sd , reg ) < < 8 ) | hdmi_read ( sd , reg + 1 ) ) & mask ;
}
2012-07-18 12:45:16 +04:00
static inline int hdmi_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_HDMI ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
2014-01-31 00:17:42 +04:00
static inline int hdmi_write_clr_set ( struct v4l2_subdev * sd , u8 reg , u8 mask , u8 val )
2013-12-10 16:45:00 +04:00
{
2014-01-31 00:17:42 +04:00
return hdmi_write ( sd , reg , ( hdmi_read ( sd , reg ) & ~ mask ) | val ) ;
2013-12-10 16:45:00 +04:00
}
2012-07-18 12:45:16 +04:00
static inline int test_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_TEST ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int cp_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV76XX_PAGE_CP , reg ) ;
2012-07-18 12:45:16 +04:00
}
2014-01-09 02:30:37 +04:00
static u16 cp_read16 ( struct v4l2_subdev * sd , u8 reg , u16 mask )
{
return ( ( cp_read ( sd , reg ) < < 8 ) | cp_read ( sd , reg + 1 ) ) & mask ;
}
2012-07-18 12:45:16 +04:00
static inline int cp_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV76XX_PAGE_CP ] , reg , val ) ;
2012-07-18 12:45:16 +04:00
}
2014-01-31 00:17:42 +04:00
static inline int cp_write_clr_set ( struct v4l2_subdev * sd , u8 reg , u8 mask , u8 val )
2012-07-18 12:45:16 +04:00
{
2014-01-31 00:17:42 +04:00
return cp_write ( sd , reg , ( cp_read ( sd , reg ) & ~ mask ) | val ) ;
2012-07-18 12:45:16 +04:00
}
static inline int vdp_read ( struct v4l2_subdev * sd , u8 reg )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return adv76xx_read_check ( state , ADV7604_PAGE_VDP , reg ) ;
2012-07-18 12:45:16 +04:00
}
static inline int vdp_write ( struct v4l2_subdev * sd , u8 reg , u8 val )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ ADV7604_PAGE_VDP ] , reg , val ) ;
2014-01-30 23:32:21 +04:00
}
2013-11-25 22:45:07 +04:00
2015-02-03 20:13:18 +03:00
# define ADV76XX_REG(page, offset) (((page) << 8) | (offset))
# define ADV76XX_REG_SEQ_TERM 0xffff
2013-11-25 22:45:07 +04:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
2015-02-03 20:13:18 +03:00
static int adv76xx_read_reg ( struct v4l2_subdev * sd , unsigned int reg )
2013-11-25 22:45:07 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-11-25 22:45:07 +04:00
unsigned int page = reg > > 8 ;
2015-06-19 16:23:06 +03:00
unsigned int val ;
int err ;
2013-11-25 22:45:07 +04:00
2017-08-04 11:07:51 +03:00
if ( page > = ADV76XX_PAGE_MAX | | ! ( BIT ( page ) & state - > info - > page_mask ) )
2013-11-25 22:45:07 +04:00
return - EINVAL ;
reg & = 0xff ;
2015-06-19 16:23:06 +03:00
err = regmap_read ( state - > regmap [ page ] , reg , & val ) ;
2013-11-25 22:45:07 +04:00
2015-06-19 16:23:06 +03:00
return err ? err : val ;
2013-11-25 22:45:07 +04:00
}
# endif
2015-02-03 20:13:18 +03:00
static int adv76xx_write_reg ( struct v4l2_subdev * sd , unsigned int reg , u8 val )
2013-11-25 22:45:07 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-11-25 22:45:07 +04:00
unsigned int page = reg > > 8 ;
2017-08-04 11:07:51 +03:00
if ( page > = ADV76XX_PAGE_MAX | | ! ( BIT ( page ) & state - > info - > page_mask ) )
2013-11-25 22:45:07 +04:00
return - EINVAL ;
reg & = 0xff ;
2015-06-19 16:23:06 +03:00
return regmap_write ( state - > regmap [ page ] , reg , val ) ;
2013-11-25 22:45:07 +04:00
}
2015-02-03 20:13:18 +03:00
static void adv76xx_write_reg_seq ( struct v4l2_subdev * sd ,
const struct adv76xx_reg_seq * reg_seq )
2013-11-25 22:45:07 +04:00
{
unsigned int i ;
2015-02-03 20:13:18 +03:00
for ( i = 0 ; reg_seq [ i ] . reg ! = ADV76XX_REG_SEQ_TERM ; i + + )
adv76xx_write_reg ( sd , reg_seq [ i ] . reg , reg_seq [ i ] . val ) ;
2013-11-25 22:45:07 +04:00
}
2014-01-27 01:42:37 +04:00
/* -----------------------------------------------------------------------------
* Format helpers
*/
2015-02-03 20:13:18 +03:00
static const struct adv76xx_format_info adv7604_formats [ ] = {
{ MEDIA_BUS_FMT_RGB888_1X24 , ADV76XX_OP_CH_SEL_RGB , true , false ,
ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV10_2X10 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_YVYU10_2X10 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_YUYV12_2X12 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YVYU12_2X12 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_UYVY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_VYUY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_UYVY10_1X20 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_VYUY10_1X20 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_YUYV10_1X20 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_YVYU10_1X20 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT } ,
{ MEDIA_BUS_FMT_UYVY12_1X24 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_VYUY12_1X24 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YUYV12_1X24 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YVYU12_1X24 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
2014-01-27 01:42:37 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct adv76xx_format_info adv7611_formats [ ] = {
{ MEDIA_BUS_FMT_RGB888_1X24 , ADV76XX_OP_CH_SEL_RGB , true , false ,
ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV12_2X12 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YVYU12_2X12 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_UYVY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_VYUY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_UYVY12_1X24 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_VYUY12_1X24 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YUYV12_1X24 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
{ MEDIA_BUS_FMT_YVYU12_1X24 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_12BIT } ,
2014-01-27 01:42:37 +04:00
} ;
2015-06-03 16:59:51 +03:00
static const struct adv76xx_format_info adv7612_formats [ ] = {
{ MEDIA_BUS_FMT_RGB888_1X24 , ADV76XX_OP_CH_SEL_RGB , true , false ,
ADV76XX_OP_MODE_SEL_SDR_444 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_2X8 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422 | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_UYVY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_VYUY8_1X16 , ADV76XX_OP_CH_SEL_RBG , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YUYV8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , false ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
{ MEDIA_BUS_FMT_YVYU8_1X16 , ADV76XX_OP_CH_SEL_RGB , false , true ,
ADV76XX_OP_MODE_SEL_SDR_422_2X | ADV76XX_OP_FORMAT_SEL_8BIT } ,
} ;
2015-02-03 20:13:18 +03:00
static const struct adv76xx_format_info *
adv76xx_format_info ( struct adv76xx_state * state , u32 code )
2014-01-27 01:42:37 +04:00
{
unsigned int i ;
for ( i = 0 ; i < state - > info - > nformats ; + + i ) {
if ( state - > info - > formats [ i ] . code = = code )
return & state - > info - > formats [ i ] ;
}
return NULL ;
}
2012-07-18 12:45:16 +04:00
/* ----------------------------------------------------------------------- */
2013-12-10 16:45:00 +04:00
static inline bool is_analog_input ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-10 16:45:00 +04:00
2014-01-29 17:08:58 +04:00
return state - > selected_input = = ADV7604_PAD_VGA_RGB | |
state - > selected_input = = ADV7604_PAD_VGA_COMP ;
2013-12-10 16:45:00 +04:00
}
static inline bool is_digital_input ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-10 16:45:00 +04:00
2015-02-03 20:13:18 +03:00
return state - > selected_input = = ADV76XX_PAD_HDMI_PORT_A | |
2014-01-29 17:08:58 +04:00
state - > selected_input = = ADV7604_PAD_HDMI_PORT_B | |
state - > selected_input = = ADV7604_PAD_HDMI_PORT_C | |
state - > selected_input = = ADV7604_PAD_HDMI_PORT_D ;
2013-12-10 16:45:00 +04:00
}
2016-01-27 14:04:50 +03:00
static const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = {
. type = V4L2_DV_BT_656_1120 ,
/* keep this initialization for compatibility with GCC < 4.4.6 */
. reserved = { 0 } ,
2018-11-08 12:51:51 +03:00
V4L2_INIT_BT_TIMINGS ( 640 , 1920 , 350 , 1200 , 25000000 , 170000000 ,
2016-01-27 14:04:50 +03:00
V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT ,
V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
V4L2_DV_BT_CAP_CUSTOM )
} ;
static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = {
. type = V4L2_DV_BT_656_1120 ,
/* keep this initialization for compatibility with GCC < 4.4.6 */
. reserved = { 0 } ,
2018-11-08 12:51:51 +03:00
V4L2_INIT_BT_TIMINGS ( 640 , 1920 , 350 , 1200 , 25000000 , 225000000 ,
2016-01-27 14:04:50 +03:00
V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT ,
V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING |
V4L2_DV_BT_CAP_CUSTOM )
} ;
2016-05-24 14:53:39 +03:00
/*
* Return the DV timings capabilities for the requested sink pad . As a special
* case , pad value - 1 returns the capabilities for the currently selected input .
*/
static const struct v4l2_dv_timings_cap *
adv76xx_get_dv_timings_cap ( struct v4l2_subdev * sd , int pad )
2016-01-27 14:04:50 +03:00
{
2016-05-24 14:53:39 +03:00
if ( pad = = - 1 ) {
struct adv76xx_state * state = to_state ( sd ) ;
pad = state - > selected_input ;
}
switch ( pad ) {
case ADV76XX_PAD_HDMI_PORT_A :
case ADV7604_PAD_HDMI_PORT_B :
case ADV7604_PAD_HDMI_PORT_C :
case ADV7604_PAD_HDMI_PORT_D :
return & adv76xx_timings_cap_digital ;
case ADV7604_PAD_VGA_RGB :
case ADV7604_PAD_VGA_COMP :
default :
return & adv7604_timings_cap_analog ;
}
2016-01-27 14:04:50 +03:00
}
2013-12-10 16:45:00 +04:00
/* ----------------------------------------------------------------------- */
2012-07-18 12:45:16 +04:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
2015-02-03 20:13:18 +03:00
static void adv76xx_inv_register ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
v4l2_info ( sd , " 0x000-0x0ff: IO Map \n " ) ;
v4l2_info ( sd , " 0x100-0x1ff: AVLink Map \n " ) ;
v4l2_info ( sd , " 0x200-0x2ff: CEC Map \n " ) ;
v4l2_info ( sd , " 0x300-0x3ff: InfoFrame Map \n " ) ;
v4l2_info ( sd , " 0x400-0x4ff: ESDP Map \n " ) ;
v4l2_info ( sd , " 0x500-0x5ff: DPP Map \n " ) ;
v4l2_info ( sd , " 0x600-0x6ff: AFE Map \n " ) ;
v4l2_info ( sd , " 0x700-0x7ff: Repeater Map \n " ) ;
v4l2_info ( sd , " 0x800-0x8ff: EDID Map \n " ) ;
v4l2_info ( sd , " 0x900-0x9ff: HDMI Map \n " ) ;
v4l2_info ( sd , " 0xa00-0xaff: Test Map \n " ) ;
v4l2_info ( sd , " 0xb00-0xbff: CP Map \n " ) ;
v4l2_info ( sd , " 0xc00-0xcff: VDP Map \n " ) ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_g_register ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_dbg_register * reg )
{
2013-11-25 22:45:07 +04:00
int ret ;
2015-02-03 20:13:18 +03:00
ret = adv76xx_read_reg ( sd , reg - > reg ) ;
2013-11-25 22:45:07 +04:00
if ( ret < 0 ) {
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " Register %03llx not supported \n " , reg - > reg ) ;
2015-02-03 20:13:18 +03:00
adv76xx_inv_register ( sd ) ;
2013-11-25 22:45:07 +04:00
return ret ;
2012-07-18 12:45:16 +04:00
}
2013-11-25 22:45:07 +04:00
reg - > size = 1 ;
reg - > val = ret ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_s_register ( struct v4l2_subdev * sd ,
2013-03-24 15:28:46 +04:00
const struct v4l2_dbg_register * reg )
2012-07-18 12:45:16 +04:00
{
2013-11-25 22:45:07 +04:00
int ret ;
2013-12-10 17:02:43 +04:00
2015-02-03 20:13:18 +03:00
ret = adv76xx_write_reg ( sd , reg - > reg , reg - > val ) ;
2013-11-25 22:45:07 +04:00
if ( ret < 0 ) {
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " Register %03llx not supported \n " , reg - > reg ) ;
2015-02-03 20:13:18 +03:00
adv76xx_inv_register ( sd ) ;
2013-11-25 22:45:07 +04:00
return ret ;
2012-07-18 12:45:16 +04:00
}
2013-11-25 22:45:07 +04:00
2012-07-18 12:45:16 +04:00
return 0 ;
}
# endif
2013-11-25 22:45:07 +04:00
static unsigned int adv7604_read_cable_det ( struct v4l2_subdev * sd )
{
u8 value = io_read ( sd , 0x6f ) ;
return ( ( value & 0x10 ) > > 4 )
| ( ( value & 0x08 ) > > 2 )
| ( ( value & 0x04 ) < < 0 )
| ( ( value & 0x02 ) < < 2 ) ;
}
static unsigned int adv7611_read_cable_det ( struct v4l2_subdev * sd )
{
u8 value = io_read ( sd , 0x6f ) ;
return value & 1 ;
}
2015-07-23 15:21:34 +03:00
static unsigned int adv7612_read_cable_det ( struct v4l2_subdev * sd )
{
/* Reads CABLE_DET_A_RAW. For input B support, need to
* account for bit 7 [ MSB ] of 0x6a ( ie . CABLE_DET_B_RAW )
*/
u8 value = io_read ( sd , 0x6f ) ;
return value & 1 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_s_detect_tx_5v_ctrl ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2015-09-07 14:12:57 +03:00
u16 cable_det = info - > read_cable_det ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-09-07 14:12:57 +03:00
return v4l2_ctrl_s_ctrl ( state - > detect_tx_5v_ctrl , cable_det ) ;
2012-07-18 12:45:16 +04:00
}
2012-10-16 17:02:05 +04:00
static int find_and_set_predefined_video_timings ( struct v4l2_subdev * sd ,
u8 prim_mode ,
2015-02-03 20:13:18 +03:00
const struct adv76xx_video_standards * predef_vid_timings ,
2012-10-16 17:02:05 +04:00
const struct v4l2_dv_timings * timings )
{
int i ;
for ( i = 0 ; predef_vid_timings [ i ] . timings . bt . width ; i + + ) {
2013-08-15 15:28:47 +04:00
if ( ! v4l2_match_dv_timings ( timings , & predef_vid_timings [ i ] . timings ,
2015-11-13 14:46:26 +03:00
is_digital_input ( sd ) ? 250000 : 1000000 , false ) )
2012-10-16 17:02:05 +04:00
continue ;
io_write ( sd , 0x00 , predef_vid_timings [ i ] . vid_std ) ; /* video std */
io_write ( sd , 0x01 , ( predef_vid_timings [ i ] . v_freq < < 4 ) +
prim_mode ) ; /* v_freq and prim mode */
return 0 ;
}
return - 1 ;
}
static int configure_predefined_video_timings ( struct v4l2_subdev * sd ,
struct v4l2_dv_timings * timings )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-10-16 17:02:05 +04:00
int err ;
v4l2_dbg ( 1 , debug , sd , " %s " , __func__ ) ;
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) ) {
2013-11-25 22:45:07 +04:00
/* reset to default values */
io_write ( sd , 0x16 , 0x43 ) ;
io_write ( sd , 0x17 , 0x5a ) ;
}
2012-10-16 17:02:05 +04:00
/* disable embedded syncs for auto graphics mode */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x81 , 0x10 , 0x00 ) ;
2012-10-16 17:02:05 +04:00
cp_write ( sd , 0x8f , 0x00 ) ;
cp_write ( sd , 0x90 , 0x00 ) ;
cp_write ( sd , 0xa2 , 0x00 ) ;
cp_write ( sd , 0xa3 , 0x00 ) ;
cp_write ( sd , 0xa4 , 0x00 ) ;
cp_write ( sd , 0xa5 , 0x00 ) ;
cp_write ( sd , 0xa6 , 0x00 ) ;
cp_write ( sd , 0xa7 , 0x00 ) ;
cp_write ( sd , 0xab , 0x00 ) ;
cp_write ( sd , 0xac , 0x00 ) ;
2013-12-10 16:45:00 +04:00
if ( is_analog_input ( sd ) ) {
2012-10-16 17:02:05 +04:00
err = find_and_set_predefined_video_timings ( sd ,
0x01 , adv7604_prim_mode_comp , timings ) ;
if ( err )
err = find_and_set_predefined_video_timings ( sd ,
0x02 , adv7604_prim_mode_gr , timings ) ;
2013-12-10 16:45:00 +04:00
} else if ( is_digital_input ( sd ) ) {
2012-10-16 17:02:05 +04:00
err = find_and_set_predefined_video_timings ( sd ,
2015-02-03 20:13:18 +03:00
0x05 , adv76xx_prim_mode_hdmi_comp , timings ) ;
2012-10-16 17:02:05 +04:00
if ( err )
err = find_and_set_predefined_video_timings ( sd ,
2015-02-03 20:13:18 +03:00
0x06 , adv76xx_prim_mode_hdmi_gr , timings ) ;
2013-12-10 16:45:00 +04:00
} else {
v4l2_dbg ( 2 , debug , sd , " %s: Unknown port %d selected \n " ,
__func__ , state - > selected_input ) ;
2012-10-16 17:02:05 +04:00
err = - 1 ;
}
return err ;
}
static void configure_custom_video_timings ( struct v4l2_subdev * sd ,
const struct v4l2_bt_timings * bt )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-10-16 17:02:05 +04:00
u32 width = htotal ( bt ) ;
u32 height = vtotal ( bt ) ;
u16 cp_start_sav = bt - > hsync + bt - > hbackporch - 4 ;
u16 cp_start_eav = width - bt - > hfrontporch ;
u16 cp_start_vbi = height - bt - > vfrontporch ;
u16 cp_end_vbi = bt - > vsync + bt - > vbackporch ;
u16 ch1_fr_ll = ( ( ( u32 ) bt - > pixelclock / 100 ) > 0 ) ?
2015-02-03 20:13:18 +03:00
( ( width * ( ADV76XX_FSC / 100 ) ) / ( ( u32 ) bt - > pixelclock / 100 ) ) : 0 ;
2012-10-16 17:02:05 +04:00
const u8 pll [ 2 ] = {
0xc0 | ( ( width > > 8 ) & 0x1f ) ,
width & 0xff
} ;
2012-07-18 12:45:16 +04:00
v4l2_dbg ( 2 , debug , sd , " %s \n " , __func__ ) ;
2013-12-10 16:45:00 +04:00
if ( is_analog_input ( sd ) ) {
2012-10-16 17:02:05 +04:00
/* auto graphics */
io_write ( sd , 0x00 , 0x07 ) ; /* video std */
io_write ( sd , 0x01 , 0x02 ) ; /* prim mode */
/* enable embedded syncs for auto graphics mode */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x81 , 0x10 , 0x10 ) ;
2012-07-18 12:45:16 +04:00
2012-10-16 17:02:05 +04:00
/* Should only be set in auto-graphics mode [REF_02, p. 91-92] */
2012-07-18 12:45:16 +04:00
/* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
/* IO-map reg. 0x16 and 0x17 should be written in sequence */
2015-06-19 16:23:06 +03:00
if ( regmap_raw_write ( state - > regmap [ ADV76XX_PAGE_IO ] ,
0x16 , pll , 2 ) )
2012-07-18 12:45:16 +04:00
v4l2_err ( sd , " writing to reg 0x16 and 0x17 failed \n " ) ;
/* active video - horizontal timing */
cp_write ( sd , 0xa2 , ( cp_start_sav > > 4 ) & 0xff ) ;
2012-10-16 17:02:05 +04:00
cp_write ( sd , 0xa3 , ( ( cp_start_sav & 0x0f ) < < 4 ) |
2013-12-10 16:45:00 +04:00
( ( cp_start_eav > > 8 ) & 0x0f ) ) ;
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0xa4 , cp_start_eav & 0xff ) ;
/* active video - vertical timing */
cp_write ( sd , 0xa5 , ( cp_start_vbi > > 4 ) & 0xff ) ;
2012-10-16 17:02:05 +04:00
cp_write ( sd , 0xa6 , ( ( cp_start_vbi & 0xf ) < < 4 ) |
2013-12-10 16:45:00 +04:00
( ( cp_end_vbi > > 8 ) & 0xf ) ) ;
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0xa7 , cp_end_vbi & 0xff ) ;
2013-12-10 16:45:00 +04:00
} else if ( is_digital_input ( sd ) ) {
2012-10-16 17:02:05 +04:00
/* set default prim_mode/vid_std for HDMI
2013-10-21 04:34:01 +04:00
according to [ REF_03 , c . 4.2 ] */
2012-10-16 17:02:05 +04:00
io_write ( sd , 0x00 , 0x02 ) ; /* video std */
io_write ( sd , 0x01 , 0x06 ) ; /* prim mode */
2013-12-10 16:45:00 +04:00
} else {
v4l2_dbg ( 2 , debug , sd , " %s: Unknown port %d selected \n " ,
__func__ , state - > selected_input ) ;
2012-07-18 12:45:16 +04:00
}
2012-10-16 17:02:05 +04:00
cp_write ( sd , 0x8f , ( ch1_fr_ll > > 8 ) & 0x7 ) ;
cp_write ( sd , 0x90 , ch1_fr_ll & 0xff ) ;
cp_write ( sd , 0xab , ( height > > 4 ) & 0xff ) ;
cp_write ( sd , 0xac , ( height & 0x0f ) < < 4 ) ;
}
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
static void adv76xx_set_offset ( struct v4l2_subdev * sd , bool auto_offset , u16 offset_a , u16 offset_b , u16 offset_c )
2013-12-05 17:39:04 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-05 17:39:04 +04:00
u8 offset_buf [ 4 ] ;
if ( auto_offset ) {
offset_a = 0x3ff ;
offset_b = 0x3ff ;
offset_c = 0x3ff ;
}
v4l2_dbg ( 2 , debug , sd , " %s: %s offset: a = 0x%x, b = 0x%x, c = 0x%x \n " ,
__func__ , auto_offset ? " Auto " : " Manual " ,
offset_a , offset_b , offset_c ) ;
offset_buf [ 0 ] = ( cp_read ( sd , 0x77 ) & 0xc0 ) | ( ( offset_a & 0x3f0 ) > > 4 ) ;
offset_buf [ 1 ] = ( ( offset_a & 0x00f ) < < 4 ) | ( ( offset_b & 0x3c0 ) > > 6 ) ;
offset_buf [ 2 ] = ( ( offset_b & 0x03f ) < < 2 ) | ( ( offset_c & 0x300 ) > > 8 ) ;
offset_buf [ 3 ] = offset_c & 0x0ff ;
/* Registers must be written in this order with no i2c access in between */
2015-06-19 16:23:06 +03:00
if ( regmap_raw_write ( state - > regmap [ ADV76XX_PAGE_CP ] ,
0x77 , offset_buf , 4 ) )
2013-12-05 17:39:04 +04:00
v4l2_err ( sd , " %s: i2c error writing to CP reg 0x77, 0x78, 0x79, 0x7a \n " , __func__ ) ;
}
2015-02-03 20:13:18 +03:00
static void adv76xx_set_gain ( struct v4l2_subdev * sd , bool auto_gain , u16 gain_a , u16 gain_b , u16 gain_c )
2013-12-05 17:39:04 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-05 17:39:04 +04:00
u8 gain_buf [ 4 ] ;
u8 gain_man = 1 ;
u8 agc_mode_man = 1 ;
if ( auto_gain ) {
gain_man = 0 ;
agc_mode_man = 0 ;
gain_a = 0x100 ;
gain_b = 0x100 ;
gain_c = 0x100 ;
}
v4l2_dbg ( 2 , debug , sd , " %s: %s gain: a = 0x%x, b = 0x%x, c = 0x%x \n " ,
__func__ , auto_gain ? " Auto " : " Manual " ,
gain_a , gain_b , gain_c ) ;
gain_buf [ 0 ] = ( ( gain_man < < 7 ) | ( agc_mode_man < < 6 ) | ( ( gain_a & 0x3f0 ) > > 4 ) ) ;
gain_buf [ 1 ] = ( ( ( gain_a & 0x00f ) < < 4 ) | ( ( gain_b & 0x3c0 ) > > 6 ) ) ;
gain_buf [ 2 ] = ( ( ( gain_b & 0x03f ) < < 2 ) | ( ( gain_c & 0x300 ) > > 8 ) ) ;
gain_buf [ 3 ] = ( ( gain_c & 0x0ff ) ) ;
/* Registers must be written in this order with no i2c access in between */
2015-06-19 16:23:06 +03:00
if ( regmap_raw_write ( state - > regmap [ ADV76XX_PAGE_CP ] ,
0x73 , gain_buf , 4 ) )
2013-12-05 17:39:04 +04:00
v4l2_err ( sd , " %s: i2c error writing to CP reg 0x73, 0x74, 0x75, 0x76 \n " , __func__ ) ;
}
2012-07-18 12:45:16 +04:00
static void set_rgb_quantization_range ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-05 17:39:04 +04:00
bool rgb_output = io_read ( sd , 0x02 ) & 0x02 ;
bool hdmi_signal = hdmi_read ( sd , 0x05 ) & 0x80 ;
2016-06-28 17:43:01 +03:00
u8 y = HDMI_COLORSPACE_RGB ;
if ( hdmi_signal & & ( io_read ( sd , 0x60 ) & 1 ) )
y = infoframe_read ( sd , 0x01 ) > > 5 ;
2013-12-05 17:39:04 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: RGB quantization range: %d, RGB out: %d, HDMI: %d \n " ,
__func__ , state - > rgb_quantization_range ,
rgb_output , hdmi_signal ) ;
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
adv76xx_set_gain ( sd , true , 0x0 , 0x0 , 0x0 ) ;
adv76xx_set_offset ( sd , true , 0x0 , 0x0 , 0x0 ) ;
2016-06-28 17:43:01 +03:00
io_write_clr_set ( sd , 0x02 , 0x04 , rgb_output ? 0 : 4 ) ;
2013-12-05 17:05:58 +04:00
2012-07-18 12:45:16 +04:00
switch ( state - > rgb_quantization_range ) {
case V4L2_DV_RGB_RANGE_AUTO :
2014-01-29 17:08:58 +04:00
if ( state - > selected_input = = ADV7604_PAD_VGA_RGB ) {
2013-12-05 17:05:58 +04:00
/* Receiving analog RGB signal
* Set RGB full range ( 0 - 255 ) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x10 ) ;
2013-12-05 17:05:58 +04:00
break ;
}
2014-01-29 17:08:58 +04:00
if ( state - > selected_input = = ADV7604_PAD_VGA_COMP ) {
2013-12-05 17:05:58 +04:00
/* Receiving analog YPbPr signal
* Set automode */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0xf0 ) ;
2013-12-05 17:05:58 +04:00
break ;
}
2013-12-05 17:39:04 +04:00
if ( hdmi_signal ) {
2013-12-05 17:05:58 +04:00
/* Receiving HDMI signal
* Set automode */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0xf0 ) ;
2013-12-05 17:05:58 +04:00
break ;
}
/* Receiving DVI-D signal
* ADV7604 selects RGB limited range regardless of
* input format ( CE / IT ) in automatic mode */
2015-03-20 20:05:05 +03:00
if ( state - > timings . bt . flags & V4L2_DV_FL_IS_CE_VIDEO ) {
2013-12-05 17:05:58 +04:00
/* RGB limited range (16-235) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x00 ) ;
2013-12-05 17:05:58 +04:00
} else {
/* RGB full range (0-255) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x10 ) ;
2013-12-05 17:39:04 +04:00
if ( is_digital_input ( sd ) & & rgb_output ) {
2015-02-03 20:13:18 +03:00
adv76xx_set_offset ( sd , false , 0x40 , 0x40 , 0x40 ) ;
2013-12-05 17:39:04 +04:00
} else {
2015-02-03 20:13:18 +03:00
adv76xx_set_gain ( sd , false , 0xe0 , 0xe0 , 0xe0 ) ;
adv76xx_set_offset ( sd , false , 0x70 , 0x70 , 0x70 ) ;
2013-12-05 17:39:04 +04:00
}
2012-07-18 12:45:16 +04:00
}
break ;
case V4L2_DV_RGB_RANGE_LIMITED :
2014-01-29 17:08:58 +04:00
if ( state - > selected_input = = ADV7604_PAD_VGA_COMP ) {
2013-12-05 17:17:15 +04:00
/* YCrCb limited range (16-235) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x20 ) ;
2013-12-05 17:39:04 +04:00
break ;
2013-12-05 17:17:15 +04:00
}
2013-12-05 17:39:04 +04:00
2016-06-28 17:43:01 +03:00
if ( y ! = HDMI_COLORSPACE_RGB )
break ;
2013-12-05 17:39:04 +04:00
/* RGB limited range (16-235) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x00 ) ;
2013-12-05 17:39:04 +04:00
2012-07-18 12:45:16 +04:00
break ;
case V4L2_DV_RGB_RANGE_FULL :
2014-01-29 17:08:58 +04:00
if ( state - > selected_input = = ADV7604_PAD_VGA_COMP ) {
2013-12-05 17:17:15 +04:00
/* YCrCb full range (0-255) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x60 ) ;
2013-12-05 17:39:04 +04:00
break ;
}
2016-06-28 17:43:01 +03:00
if ( y ! = HDMI_COLORSPACE_RGB )
break ;
2013-12-05 17:39:04 +04:00
/* RGB full range (0-255) */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0xf0 , 0x10 ) ;
2013-12-05 17:39:04 +04:00
if ( is_analog_input ( sd ) | | hdmi_signal )
break ;
/* Adjust gain/offset for DVI-D signals only */
if ( rgb_output ) {
2015-02-03 20:13:18 +03:00
adv76xx_set_offset ( sd , false , 0x40 , 0x40 , 0x40 ) ;
2013-12-05 17:17:15 +04:00
} else {
2015-02-03 20:13:18 +03:00
adv76xx_set_gain ( sd , false , 0xe0 , 0xe0 , 0xe0 ) ;
adv76xx_set_offset ( sd , false , 0x70 , 0x70 , 0x70 ) ;
2013-12-05 17:17:15 +04:00
}
2012-07-18 12:45:16 +04:00
break ;
}
}
2015-02-03 20:13:18 +03:00
static int adv76xx_s_ctrl ( struct v4l2_ctrl * ctrl )
2012-07-18 12:45:16 +04:00
{
2014-01-30 22:16:03 +04:00
struct v4l2_subdev * sd =
2015-02-03 20:13:18 +03:00
& container_of ( ctrl - > handler , struct adv76xx_state , hdl ) - > sd ;
2014-01-30 22:16:03 +04:00
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
cp_write ( sd , 0x3c , ctrl - > val ) ;
return 0 ;
case V4L2_CID_CONTRAST :
cp_write ( sd , 0x3a , ctrl - > val ) ;
return 0 ;
case V4L2_CID_SATURATION :
cp_write ( sd , 0x3b , ctrl - > val ) ;
return 0 ;
case V4L2_CID_HUE :
cp_write ( sd , 0x3d , ctrl - > val ) ;
return 0 ;
case V4L2_CID_DV_RX_RGB_RANGE :
state - > rgb_quantization_range = ctrl - > val ;
set_rgb_quantization_range ( sd ) ;
return 0 ;
case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE :
2015-02-03 20:13:18 +03:00
if ( ! adv76xx_has_afe ( state ) )
2013-11-25 22:45:07 +04:00
return - EINVAL ;
2012-07-18 12:45:16 +04:00
/* Set the analog sampling phase. This is needed to find the
best sampling phase for analog video : an application or
driver has to try a number of phases and analyze the picture
quality before settling on the best performing phase . */
afe_write ( sd , 0xc8 , ctrl - > val ) ;
return 0 ;
case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL :
/* Use the default blue color for free running mode,
or supply your own . */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0xbf , 0x04 , ctrl - > val < < 2 ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
case V4L2_CID_ADV_RX_FREE_RUN_COLOR :
cp_write ( sd , 0xc0 , ( ctrl - > val & 0xff0000 ) > > 16 ) ;
cp_write ( sd , 0xc1 , ( ctrl - > val & 0x00ff00 ) > > 8 ) ;
cp_write ( sd , 0xc2 , ( u8 ) ( ctrl - > val & 0x0000ff ) ) ;
return 0 ;
}
return - EINVAL ;
}
2016-01-27 16:31:41 +03:00
static int adv76xx_g_volatile_ctrl ( struct v4l2_ctrl * ctrl )
{
struct v4l2_subdev * sd =
& container_of ( ctrl - > handler , struct adv76xx_state , hdl ) - > sd ;
if ( ctrl - > id = = V4L2_CID_DV_RX_IT_CONTENT_TYPE ) {
ctrl - > val = V4L2_DV_IT_CONTENT_TYPE_NO_ITC ;
if ( ( io_read ( sd , 0x60 ) & 1 ) & & ( infoframe_read ( sd , 0x03 ) & 0x80 ) )
ctrl - > val = ( infoframe_read ( sd , 0x05 ) > > 4 ) & 3 ;
return 0 ;
}
return - EINVAL ;
}
2012-07-18 12:45:16 +04:00
/* ----------------------------------------------------------------------- */
static inline bool no_power ( struct v4l2_subdev * sd )
{
/* Entire chip or CP powered off */
return io_read ( sd , 0x0c ) & 0x24 ;
}
static inline bool no_signal_tmds ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-10 16:45:00 +04:00
return ! ( io_read ( sd , 0x6a ) & ( 0x10 > > state - > selected_input ) ) ;
2012-07-18 12:45:16 +04:00
}
static inline bool no_lock_tmds ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2013-11-25 22:45:07 +04:00
return ( io_read ( sd , 0x6a ) & info - > tdms_lock_mask ) ! = info - > tdms_lock_mask ;
2012-07-18 12:45:16 +04:00
}
2013-08-14 15:52:46 +04:00
static inline bool is_hdmi ( struct v4l2_subdev * sd )
{
return hdmi_read ( sd , 0x05 ) & 0x80 ;
}
2012-07-18 12:45:16 +04:00
static inline bool no_lock_sspd ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-11-25 22:45:07 +04:00
/*
* Chips without a AFE don ' t expose registers for the SSPD , so just assume
* that we have a lock .
*/
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) )
2013-11-25 22:45:07 +04:00
return false ;
2012-07-18 12:45:16 +04:00
/* TODO channel 2 */
return ( ( cp_read ( sd , 0xb5 ) & 0xd0 ) ! = 0xd0 ) ;
}
static inline bool no_lock_stdi ( struct v4l2_subdev * sd )
{
/* TODO channel 2 */
return ! ( cp_read ( sd , 0xb1 ) & 0x80 ) ;
}
static inline bool no_signal ( struct v4l2_subdev * sd )
{
bool ret ;
ret = no_power ( sd ) ;
ret | = no_lock_stdi ( sd ) ;
ret | = no_lock_sspd ( sd ) ;
2013-12-10 16:45:00 +04:00
if ( is_digital_input ( sd ) ) {
2012-07-18 12:45:16 +04:00
ret | = no_lock_tmds ( sd ) ;
ret | = no_signal_tmds ( sd ) ;
}
return ret ;
}
static inline bool no_lock_cp ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-11-25 22:45:07 +04:00
2015-02-03 20:13:18 +03:00
if ( ! adv76xx_has_afe ( state ) )
2013-11-25 22:45:07 +04:00
return false ;
2012-07-18 12:45:16 +04:00
/* CP has detected a non standard number of lines on the incoming
video compared to what it is configured to receive by s_dv_timings */
return io_read ( sd , 0x12 ) & 0x01 ;
}
2015-02-06 17:37:58 +03:00
static inline bool in_free_run ( struct v4l2_subdev * sd )
{
return cp_read ( sd , 0xff ) & 0x10 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_g_input_status ( struct v4l2_subdev * sd , u32 * status )
2012-07-18 12:45:16 +04:00
{
* status = 0 ;
* status | = no_power ( sd ) ? V4L2_IN_ST_NO_POWER : 0 ;
* status | = no_signal ( sd ) ? V4L2_IN_ST_NO_SIGNAL : 0 ;
2015-02-06 17:37:58 +03:00
if ( ! in_free_run ( sd ) & & no_lock_cp ( sd ) )
* status | = is_digital_input ( sd ) ?
V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK ;
2012-07-18 12:45:16 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: status = 0x%x \n " , __func__ , * status ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
struct stdi_readback {
u16 bl , lcf , lcvs ;
u8 hs_pol , vs_pol ;
bool interlaced ;
} ;
static int stdi2dv_timings ( struct v4l2_subdev * sd ,
struct stdi_readback * stdi ,
struct v4l2_dv_timings * timings )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
u32 hfreq = ( ADV76XX_FSC * 8 ) / stdi - > bl ;
2012-07-18 12:45:16 +04:00
u32 pix_clk ;
int i ;
2016-01-27 14:04:50 +03:00
for ( i = 0 ; v4l2_dv_timings_presets [ i ] . bt . width ; i + + ) {
const struct v4l2_bt_timings * bt = & v4l2_dv_timings_presets [ i ] . bt ;
if ( ! v4l2_valid_dv_timings ( & v4l2_dv_timings_presets [ i ] ,
2016-05-24 14:53:39 +03:00
adv76xx_get_dv_timings_cap ( sd , - 1 ) ,
2016-01-27 14:04:50 +03:00
adv76xx_check_dv_timings , NULL ) )
2012-07-18 12:45:16 +04:00
continue ;
2016-01-27 14:04:50 +03:00
if ( vtotal ( bt ) ! = stdi - > lcf + 1 )
continue ;
if ( bt - > vsync ! = stdi - > lcvs )
2012-07-18 12:45:16 +04:00
continue ;
2016-01-27 14:04:50 +03:00
pix_clk = hfreq * htotal ( bt ) ;
2012-07-18 12:45:16 +04:00
2016-01-27 14:04:50 +03:00
if ( ( pix_clk < bt - > pixelclock + 1000000 ) & &
( pix_clk > bt - > pixelclock - 1000000 ) ) {
* timings = v4l2_dv_timings_presets [ i ] ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
}
2015-06-10 19:51:42 +03:00
if ( v4l2_detect_cvt ( stdi - > lcf + 1 , hfreq , stdi - > lcvs , 0 ,
2012-07-18 12:45:16 +04:00
( stdi - > hs_pol = = ' + ' ? V4L2_DV_HSYNC_POS_POL : 0 ) |
( stdi - > vs_pol = = ' + ' ? V4L2_DV_VSYNC_POS_POL : 0 ) ,
2015-05-22 08:27:34 +03:00
false , timings ) )
2012-07-18 12:45:16 +04:00
return 0 ;
if ( v4l2_detect_gtf ( stdi - > lcf + 1 , hfreq , stdi - > lcvs ,
( stdi - > hs_pol = = ' + ' ? V4L2_DV_HSYNC_POS_POL : 0 ) |
( stdi - > vs_pol = = ' + ' ? V4L2_DV_VSYNC_POS_POL : 0 ) ,
2015-05-22 08:27:34 +03:00
false , state - > aspect_ratio , timings ) )
2012-07-18 12:45:16 +04:00
return 0 ;
2012-10-16 17:02:05 +04:00
v4l2_dbg ( 2 , debug , sd ,
" %s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync \n " ,
__func__ , stdi - > lcvs , stdi - > lcf , stdi - > bl ,
stdi - > hs_pol , stdi - > vs_pol ) ;
2012-07-18 12:45:16 +04:00
return - 1 ;
}
2013-11-25 22:45:07 +04:00
2012-07-18 12:45:16 +04:00
static int read_stdi ( struct v4l2_subdev * sd , struct stdi_readback * stdi )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2014-01-09 03:26:55 +04:00
u8 polarity ;
2012-07-18 12:45:16 +04:00
if ( no_lock_stdi ( sd ) | | no_lock_sspd ( sd ) ) {
v4l2_dbg ( 2 , debug , sd , " %s: STDI and/or SSPD not locked \n " , __func__ ) ;
return - 1 ;
}
/* read STDI */
2014-01-09 02:30:37 +04:00
stdi - > bl = cp_read16 ( sd , 0xb1 , 0x3fff ) ;
2013-11-25 22:45:07 +04:00
stdi - > lcf = cp_read16 ( sd , info - > lcf_reg , 0x7ff ) ;
2012-07-18 12:45:16 +04:00
stdi - > lcvs = cp_read ( sd , 0xb3 ) > > 3 ;
stdi - > interlaced = io_read ( sd , 0x12 ) & 0x10 ;
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) ) {
2013-11-25 22:45:07 +04:00
/* read SSPD */
polarity = cp_read ( sd , 0xb5 ) ;
if ( ( polarity & 0x03 ) = = 0x01 ) {
stdi - > hs_pol = polarity & 0x10
? ( polarity & 0x08 ? ' + ' : ' - ' ) : ' x ' ;
stdi - > vs_pol = polarity & 0x40
? ( polarity & 0x20 ? ' + ' : ' - ' ) : ' x ' ;
} else {
stdi - > hs_pol = ' x ' ;
stdi - > vs_pol = ' x ' ;
}
2012-07-18 12:45:16 +04:00
} else {
2013-11-25 22:45:07 +04:00
polarity = hdmi_read ( sd , 0x05 ) ;
stdi - > hs_pol = polarity & 0x20 ? ' + ' : ' - ' ;
stdi - > vs_pol = polarity & 0x10 ? ' + ' : ' - ' ;
2012-07-18 12:45:16 +04:00
}
if ( no_lock_stdi ( sd ) | | no_lock_sspd ( sd ) ) {
v4l2_dbg ( 2 , debug , sd ,
" %s: signal lost during readout of STDI/SSPD \n " , __func__ ) ;
return - 1 ;
}
if ( stdi - > lcf < 239 | | stdi - > bl < 8 | | stdi - > bl = = 0x3fff ) {
v4l2_dbg ( 2 , debug , sd , " %s: invalid signal \n " , __func__ ) ;
memset ( stdi , 0 , sizeof ( struct stdi_readback ) ) ;
return - 1 ;
}
v4l2_dbg ( 2 , debug , sd ,
" %s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s \n " ,
__func__ , stdi - > lcf , stdi - > bl , stdi - > lcvs ,
stdi - > hs_pol , stdi - > vs_pol ,
stdi - > interlaced ? " interlaced " : " progressive " ) ;
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_enum_dv_timings ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_enum_dv_timings * timings )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2014-01-29 17:09:41 +04:00
if ( timings - > pad > = state - > source_pad )
return - EINVAL ;
2016-01-27 14:04:50 +03:00
return v4l2_enum_dv_timings_cap ( timings ,
2016-05-24 14:53:39 +03:00
adv76xx_get_dv_timings_cap ( sd , timings - > pad ) ,
adv76xx_check_dv_timings , NULL ) ;
2012-07-18 12:45:16 +04:00
}
2015-02-03 20:13:18 +03:00
static int adv76xx_dv_timings_cap ( struct v4l2_subdev * sd ,
2014-01-31 15:51:18 +04:00
struct v4l2_dv_timings_cap * cap )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2016-05-24 14:53:39 +03:00
unsigned int pad = cap - > pad ;
2014-01-31 15:51:18 +04:00
if ( cap - > pad > = state - > source_pad )
return - EINVAL ;
2016-05-24 14:53:39 +03:00
* cap = * adv76xx_get_dv_timings_cap ( sd , pad ) ;
cap - > pad = pad ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
2015-02-03 20:13:18 +03:00
if the format is listed in adv76xx_timings [ ] */
static void adv76xx_fill_optional_dv_timings_fields ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings * timings )
{
2016-05-24 14:53:39 +03:00
v4l2_find_dv_timings_cap ( timings , adv76xx_get_dv_timings_cap ( sd , - 1 ) ,
is_digital_input ( sd ) ? 250000 : 1000000 ,
adv76xx_check_dv_timings , NULL ) ;
2012-07-18 12:45:16 +04:00
}
2013-11-25 22:45:07 +04:00
static unsigned int adv7604_read_hdmi_pixelclock ( struct v4l2_subdev * sd )
{
unsigned int freq ;
int a , b ;
a = hdmi_read ( sd , 0x06 ) ;
b = hdmi_read ( sd , 0x3b ) ;
if ( a < 0 | | b < 0 )
return 0 ;
freq = a * 1000000 + ( ( b & 0x30 ) > > 4 ) * 250000 ;
if ( is_hdmi ( sd ) ) {
/* adjust for deep color mode */
unsigned bits_per_channel = ( ( hdmi_read ( sd , 0x0b ) & 0x60 ) > > 4 ) + 8 ;
freq = freq * 8 / bits_per_channel ;
}
return freq ;
}
static unsigned int adv7611_read_hdmi_pixelclock ( struct v4l2_subdev * sd )
{
int a , b ;
a = hdmi_read ( sd , 0x51 ) ;
b = hdmi_read ( sd , 0x52 ) ;
if ( a < 0 | | b < 0 )
return 0 ;
return ( ( a < < 1 ) | ( b > > 7 ) ) * 1000000 + ( b & 0x7f ) * 1000000 / 128 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_query_dv_timings ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings * timings )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2012-07-18 12:45:16 +04:00
struct v4l2_bt_timings * bt = & timings - > bt ;
struct stdi_readback stdi ;
if ( ! timings )
return - EINVAL ;
memset ( timings , 0 , sizeof ( struct v4l2_dv_timings ) ) ;
if ( no_signal ( sd ) ) {
2013-12-05 17:34:46 +04:00
state - > restart_stdi_once = true ;
2012-07-18 12:45:16 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: no valid signal \n " , __func__ ) ;
return - ENOLINK ;
}
/* read STDI */
if ( read_stdi ( sd , & stdi ) ) {
v4l2_dbg ( 1 , debug , sd , " %s: STDI/SSPD not locked \n " , __func__ ) ;
return - ENOLINK ;
}
bt - > interlaced = stdi . interlaced ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE ;
2013-12-10 16:45:00 +04:00
if ( is_digital_input ( sd ) ) {
2016-07-14 17:53:47 +03:00
bool hdmi_signal = hdmi_read ( sd , 0x05 ) & 0x80 ;
u8 vic = 0 ;
u32 w , h ;
w = hdmi_read16 ( sd , 0x07 , info - > linewidth_mask ) ;
h = hdmi_read16 ( sd , 0x09 , info - > field0_height_mask ) ;
if ( hdmi_signal & & ( io_read ( sd , 0x60 ) & 1 ) )
vic = infoframe_read ( sd , 0x04 ) ;
if ( vic & & v4l2_find_dv_timings_cea861_vic ( timings , vic ) & &
bt - > width = = w & & bt - > height = = h )
goto found ;
2012-07-18 12:45:16 +04:00
timings - > type = V4L2_DV_BT_656_1120 ;
2016-07-14 17:53:47 +03:00
bt - > width = w ;
bt - > height = h ;
2013-11-25 22:45:07 +04:00
bt - > pixelclock = info - > read_hdmi_pixelclock ( sd ) ;
2015-04-09 11:25:46 +03:00
bt - > hfrontporch = hdmi_read16 ( sd , 0x20 , info - > hfrontporch_mask ) ;
bt - > hsync = hdmi_read16 ( sd , 0x22 , info - > hsync_mask ) ;
bt - > hbackporch = hdmi_read16 ( sd , 0x24 , info - > hbackporch_mask ) ;
bt - > vfrontporch = hdmi_read16 ( sd , 0x2a ,
info - > field0_vfrontporch_mask ) / 2 ;
bt - > vsync = hdmi_read16 ( sd , 0x2e , info - > field0_vsync_mask ) / 2 ;
bt - > vbackporch = hdmi_read16 ( sd , 0x32 ,
info - > field0_vbackporch_mask ) / 2 ;
2012-07-18 12:45:16 +04:00
bt - > polarities = ( ( hdmi_read ( sd , 0x05 ) & 0x10 ) ? V4L2_DV_VSYNC_POS_POL : 0 ) |
( ( hdmi_read ( sd , 0x05 ) & 0x20 ) ? V4L2_DV_HSYNC_POS_POL : 0 ) ;
if ( bt - > interlaced = = V4L2_DV_INTERLACED ) {
2015-04-09 11:25:46 +03:00
bt - > height + = hdmi_read16 ( sd , 0x0b ,
info - > field1_height_mask ) ;
bt - > il_vfrontporch = hdmi_read16 ( sd , 0x2c ,
info - > field1_vfrontporch_mask ) / 2 ;
bt - > il_vsync = hdmi_read16 ( sd , 0x30 ,
info - > field1_vsync_mask ) / 2 ;
bt - > il_vbackporch = hdmi_read16 ( sd , 0x34 ,
info - > field1_vbackporch_mask ) / 2 ;
2012-07-18 12:45:16 +04:00
}
2015-02-03 20:13:18 +03:00
adv76xx_fill_optional_dv_timings_fields ( sd , timings ) ;
2012-07-18 12:45:16 +04:00
} else {
/* find format
2012-10-16 12:46:21 +04:00
* Since LCVS values are inaccurate [ REF_03 , p . 275 - 276 ] ,
2012-07-18 12:45:16 +04:00
* stdi2dv_timings ( ) is called with lcvs + - 1 if the first attempt fails .
*/
if ( ! stdi2dv_timings ( sd , & stdi , timings ) )
goto found ;
stdi . lcvs + = 1 ;
v4l2_dbg ( 1 , debug , sd , " %s: lcvs + 1 = %d \n " , __func__ , stdi . lcvs ) ;
if ( ! stdi2dv_timings ( sd , & stdi , timings ) )
goto found ;
stdi . lcvs - = 2 ;
v4l2_dbg ( 1 , debug , sd , " %s: lcvs - 1 = %d \n " , __func__ , stdi . lcvs ) ;
if ( stdi2dv_timings ( sd , & stdi , timings ) ) {
2012-10-16 17:12:55 +04:00
/*
* The STDI block may measure wrong values , especially
* for lcvs and lcf . If the driver can not find any
* valid timing , the STDI block is restarted to measure
* the video timings again . The function will return an
* error , but the restart of STDI will generate a new
* STDI interrupt and the format detection process will
* restart .
*/
if ( state - > restart_stdi_once ) {
v4l2_dbg ( 1 , debug , sd , " %s: restart STDI \n " , __func__ ) ;
/* TODO restart STDI for Sync Channel 2 */
/* enter one-shot mode */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x86 , 0x06 , 0x00 ) ;
2012-10-16 17:12:55 +04:00
/* trigger STDI restart */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x86 , 0x06 , 0x04 ) ;
2012-10-16 17:12:55 +04:00
/* reset to continuous mode */
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x86 , 0x06 , 0x02 ) ;
2012-10-16 17:12:55 +04:00
state - > restart_stdi_once = false ;
return - ENOLINK ;
}
2012-07-18 12:45:16 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: format not supported \n " , __func__ ) ;
return - ERANGE ;
}
2012-10-16 17:12:55 +04:00
state - > restart_stdi_once = true ;
2012-07-18 12:45:16 +04:00
}
found :
if ( no_signal ( sd ) ) {
v4l2_dbg ( 1 , debug , sd , " %s: signal lost during readout \n " , __func__ ) ;
memset ( timings , 0 , sizeof ( struct v4l2_dv_timings ) ) ;
return - ENOLINK ;
}
2013-12-10 16:45:00 +04:00
if ( ( is_analog_input ( sd ) & & bt - > pixelclock > 170000000 ) | |
( is_digital_input ( sd ) & & bt - > pixelclock > 225000000 ) ) {
2012-07-18 12:45:16 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: pixelclock out of range %d \n " ,
__func__ , ( u32 ) bt - > pixelclock ) ;
return - ERANGE ;
}
if ( debug > 1 )
2015-02-03 20:13:18 +03:00
v4l2_print_dv_timings ( sd - > name , " adv76xx_query_dv_timings: " ,
2013-08-15 15:05:59 +04:00
timings , true ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_s_dv_timings ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings * timings )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
struct v4l2_bt_timings * bt ;
2012-10-16 17:02:05 +04:00
int err ;
2012-07-18 12:45:16 +04:00
if ( ! timings )
return - EINVAL ;
2015-11-13 14:46:26 +03:00
if ( v4l2_match_dv_timings ( & state - > timings , timings , 0 , false ) ) {
2013-12-12 17:13:35 +04:00
v4l2_dbg ( 1 , debug , sd , " %s: no change \n " , __func__ ) ;
return 0 ;
}
2012-07-18 12:45:16 +04:00
bt = & timings - > bt ;
2016-05-24 14:53:39 +03:00
if ( ! v4l2_valid_dv_timings ( timings , adv76xx_get_dv_timings_cap ( sd , - 1 ) ,
2016-01-27 14:04:50 +03:00
adv76xx_check_dv_timings , NULL ) )
2012-07-18 12:45:16 +04:00
return - ERANGE ;
2012-10-16 17:02:05 +04:00
2015-02-03 20:13:18 +03:00
adv76xx_fill_optional_dv_timings_fields ( sd , timings ) ;
2012-07-18 12:45:16 +04:00
state - > timings = * timings ;
2014-01-31 00:17:42 +04:00
cp_write_clr_set ( sd , 0x91 , 0x40 , bt - > interlaced ? 0x40 : 0x00 ) ;
2012-10-16 17:02:05 +04:00
/* Use prim_mode and vid_std when available */
err = configure_predefined_video_timings ( sd , timings ) ;
if ( err ) {
/* custom settings when the video format
does not have prim_mode / vid_std */
configure_custom_video_timings ( sd , bt ) ;
}
2012-07-18 12:45:16 +04:00
set_rgb_quantization_range ( sd ) ;
if ( debug > 1 )
2015-02-03 20:13:18 +03:00
v4l2_print_dv_timings ( sd - > name , " adv76xx_s_dv_timings: " ,
2013-08-15 15:05:59 +04:00
timings , true ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_g_dv_timings ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings * timings )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
* timings = state - > timings ;
return 0 ;
}
2013-11-25 22:45:07 +04:00
static void adv7604_set_termination ( struct v4l2_subdev * sd , bool enable )
{
hdmi_write ( sd , 0x01 , enable ? 0x00 : 0x78 ) ;
}
static void adv7611_set_termination ( struct v4l2_subdev * sd , bool enable )
{
hdmi_write ( sd , 0x83 , enable ? 0xfe : 0xff ) ;
}
2012-10-16 13:40:45 +04:00
static void enable_input ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-10-16 13:40:45 +04:00
2013-12-10 16:45:00 +04:00
if ( is_analog_input ( sd ) ) {
2012-07-18 12:45:16 +04:00
io_write ( sd , 0x15 , 0xb0 ) ; /* Disable Tristate of Pins (no audio) */
2013-12-10 16:45:00 +04:00
} else if ( is_digital_input ( sd ) ) {
2014-01-31 00:17:42 +04:00
hdmi_write_clr_set ( sd , 0x00 , 0x03 , state - > selected_input ) ;
2013-11-25 22:45:07 +04:00
state - > info - > set_termination ( sd , true ) ;
2012-07-18 12:45:16 +04:00
io_write ( sd , 0x15 , 0xa0 ) ; /* Disable Tristate of Pins */
2014-01-31 00:17:42 +04:00
hdmi_write_clr_set ( sd , 0x1a , 0x10 , 0x00 ) ; /* Unmute audio */
2013-12-10 16:45:00 +04:00
} else {
v4l2_dbg ( 2 , debug , sd , " %s: Unknown port %d selected \n " ,
__func__ , state - > selected_input ) ;
2012-07-18 12:45:16 +04:00
}
}
static void disable_input ( struct v4l2_subdev * sd )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-11-25 22:45:07 +04:00
2014-01-31 00:17:42 +04:00
hdmi_write_clr_set ( sd , 0x1a , 0x10 , 0x10 ) ; /* Mute audio */
2013-12-05 17:33:41 +04:00
msleep ( 16 ) ; /* 512 samples with >= 32 kHz sample rate [REF_03, c. 7.16.10] */
2012-07-18 12:45:16 +04:00
io_write ( sd , 0x15 , 0xbe ) ; /* Tristate all outputs from video core */
2013-11-25 22:45:07 +04:00
state - > info - > set_termination ( sd , false ) ;
2012-07-18 12:45:16 +04:00
}
2012-10-16 13:40:45 +04:00
static void select_input ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2012-07-18 12:45:16 +04:00
2013-12-10 16:45:00 +04:00
if ( is_analog_input ( sd ) ) {
2015-02-03 20:13:18 +03:00
adv76xx_write_reg_seq ( sd , info - > recommended_settings [ 0 ] ) ;
2012-07-18 12:45:16 +04:00
afe_write ( sd , 0x00 , 0x08 ) ; /* power up ADC */
afe_write ( sd , 0x01 , 0x06 ) ; /* power up Analog Front End */
afe_write ( sd , 0xc8 , 0x00 ) ; /* phase control */
2013-12-10 16:45:00 +04:00
} else if ( is_digital_input ( sd ) ) {
hdmi_write ( sd , 0x00 , state - > selected_input & 0x03 ) ;
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
adv76xx_write_reg_seq ( sd , info - > recommended_settings [ 1 ] ) ;
2013-11-25 22:45:07 +04:00
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) ) {
2013-11-25 22:45:07 +04:00
afe_write ( sd , 0x00 , 0xff ) ; /* power down ADC */
afe_write ( sd , 0x01 , 0xfe ) ; /* power down Analog Front End */
afe_write ( sd , 0xc8 , 0x40 ) ; /* phase control */
}
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0x3e , 0x00 ) ; /* CP core pre-gain control */
cp_write ( sd , 0xc3 , 0x39 ) ; /* CP coast control. Graphics mode */
cp_write ( sd , 0x40 , 0x80 ) ; /* CP core pre-gain control. Graphics mode */
2013-12-10 16:45:00 +04:00
} else {
v4l2_dbg ( 2 , debug , sd , " %s: Unknown port %d selected \n " ,
__func__ , state - > selected_input ) ;
2012-07-18 12:45:16 +04:00
}
}
2015-02-03 20:13:18 +03:00
static int adv76xx_s_routing ( struct v4l2_subdev * sd ,
2012-07-18 12:45:16 +04:00
u32 input , u32 output , u32 config )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2013-12-05 17:24:05 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: input %d, selected input %d " ,
__func__ , input , state - > selected_input ) ;
if ( input = = state - > selected_input )
return 0 ;
2012-07-18 12:45:16 +04:00
2013-11-25 22:45:07 +04:00
if ( input > state - > info - > max_port )
return - EINVAL ;
2013-12-10 16:45:00 +04:00
state - > selected_input = input ;
2012-07-18 12:45:16 +04:00
disable_input ( sd ) ;
2012-10-16 13:40:45 +04:00
select_input ( sd ) ;
enable_input ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-24 19:50:30 +03:00
v4l2_subdev_notify_event ( sd , & adv76xx_ev_fmt ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_enum_mbus_code ( struct v4l2_subdev * sd ,
2015-03-04 12:47:54 +03:00
struct v4l2_subdev_pad_config * cfg ,
2014-01-27 01:42:37 +04:00
struct v4l2_subdev_mbus_code_enum * code )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2014-01-27 01:42:37 +04:00
if ( code - > index > = state - > info - > nformats )
2012-07-18 12:45:16 +04:00
return - EINVAL ;
2014-01-27 01:42:37 +04:00
code - > code = state - > info - > formats [ code - > index ] . code ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static void adv76xx_fill_format ( struct adv76xx_state * state ,
2014-01-27 01:42:37 +04:00
struct v4l2_mbus_framefmt * format )
2012-07-18 12:45:16 +04:00
{
2014-01-27 01:42:37 +04:00
memset ( format , 0 , sizeof ( * format ) ) ;
2012-07-18 12:45:16 +04:00
2014-01-27 01:42:37 +04:00
format - > width = state - > timings . bt . width ;
format - > height = state - > timings . bt . height ;
format - > field = V4L2_FIELD_NONE ;
2015-03-20 20:05:05 +03:00
format - > colorspace = V4L2_COLORSPACE_SRGB ;
2014-01-27 01:42:37 +04:00
2015-03-20 20:05:05 +03:00
if ( state - > timings . bt . flags & V4L2_DV_FL_IS_CE_VIDEO )
2014-01-27 01:42:37 +04:00
format - > colorspace = ( state - > timings . bt . height < = 576 ) ?
2012-07-18 12:45:16 +04:00
V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709 ;
2014-01-27 01:42:37 +04:00
}
/*
* Compute the op_ch_sel value required to obtain on the bus the component order
* corresponding to the selected format taking into account bus reordering
* applied by the board at the output of the device .
*
* The following table gives the op_ch_value from the format component order
* ( expressed as op_ch_sel value in column ) and the bus reordering ( expressed as
2015-02-03 20:13:18 +03:00
* adv76xx_bus_order value in row ) .
2014-01-27 01:42:37 +04:00
*
* | GBR ( 0 ) GRB ( 1 ) BGR ( 2 ) RGB ( 3 ) BRG ( 4 ) RBG ( 5 )
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* RGB ( NOP ) | GBR GRB BGR RGB BRG RBG
* GRB ( 1 - 2 ) | BGR RGB GBR GRB RBG BRG
* RBG ( 2 - 3 ) | GRB GBR BRG RBG BGR RGB
* BGR ( 1 - 3 ) | RBG BRG RGB BGR GRB GBR
* BRG ( ROR ) | BRG RBG GRB GBR RGB BGR
* GBR ( ROL ) | RGB BGR RBG BRG GBR GRB
*/
2015-02-03 20:13:18 +03:00
static unsigned int adv76xx_op_ch_sel ( struct adv76xx_state * state )
2014-01-27 01:42:37 +04:00
{
# define _SEL(a,b,c,d,e,f) { \
2015-02-03 20:13:18 +03:00
ADV76XX_OP_CH_SEL_ # # a , ADV76XX_OP_CH_SEL_ # # b , ADV76XX_OP_CH_SEL_ # # c , \
ADV76XX_OP_CH_SEL_ # # d , ADV76XX_OP_CH_SEL_ # # e , ADV76XX_OP_CH_SEL_ # # f }
2014-01-27 01:42:37 +04:00
# define _BUS(x) [ADV7604_BUS_ORDER_##x]
static const unsigned int op_ch_sel [ 6 ] [ 6 ] = {
_BUS ( RGB ) /* NOP */ = _SEL ( GBR , GRB , BGR , RGB , BRG , RBG ) ,
_BUS ( GRB ) /* 1-2 */ = _SEL ( BGR , RGB , GBR , GRB , RBG , BRG ) ,
_BUS ( RBG ) /* 2-3 */ = _SEL ( GRB , GBR , BRG , RBG , BGR , RGB ) ,
_BUS ( BGR ) /* 1-3 */ = _SEL ( RBG , BRG , RGB , BGR , GRB , GBR ) ,
_BUS ( BRG ) /* ROR */ = _SEL ( BRG , RBG , GRB , GBR , RGB , BGR ) ,
_BUS ( GBR ) /* ROL */ = _SEL ( RGB , BGR , RBG , BRG , GBR , GRB ) ,
} ;
return op_ch_sel [ state - > pdata . bus_order ] [ state - > format - > op_ch_sel > > 5 ] ;
}
2015-02-03 20:13:18 +03:00
static void adv76xx_setup_format ( struct adv76xx_state * state )
2014-01-27 01:42:37 +04:00
{
struct v4l2_subdev * sd = & state - > sd ;
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x02 , 0x02 ,
2015-02-03 20:13:18 +03:00
state - > format - > rgb_out ? ADV76XX_RGB_OUT : 0 ) ;
2014-01-27 01:42:37 +04:00
io_write ( sd , 0x03 , state - > format - > op_format_sel |
state - > pdata . op_format_mode_sel ) ;
2015-02-03 20:13:18 +03:00
io_write_clr_set ( sd , 0x04 , 0xe0 , adv76xx_op_ch_sel ( state ) ) ;
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x05 , 0x01 ,
2015-02-03 20:13:18 +03:00
state - > format - > swap_cb_cr ? ADV76XX_OP_SWAP_CB_CR : 0 ) ;
2016-06-28 17:43:01 +03:00
set_rgb_quantization_range ( sd ) ;
2014-01-27 01:42:37 +04:00
}
2015-03-04 12:47:54 +03:00
static int adv76xx_get_format ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
2014-01-27 01:42:37 +04:00
struct v4l2_subdev_format * format )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2014-01-27 01:42:37 +04:00
if ( format - > pad ! = state - > source_pad )
return - EINVAL ;
2015-02-03 20:13:18 +03:00
adv76xx_fill_format ( state , & format - > format ) ;
2014-01-27 01:42:37 +04:00
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY ) {
struct v4l2_mbus_framefmt * fmt ;
2015-03-04 12:47:54 +03:00
fmt = v4l2_subdev_get_try_format ( sd , cfg , format - > pad ) ;
2014-01-27 01:42:37 +04:00
format - > format . code = fmt - > code ;
} else {
format - > format . code = state - > format - > code ;
2012-07-18 12:45:16 +04:00
}
2014-01-27 01:42:37 +04:00
return 0 ;
}
2015-12-22 17:22:01 +03:00
static int adv76xx_get_selection ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_selection * sel )
{
struct adv76xx_state * state = to_state ( sd ) ;
if ( sel - > which ! = V4L2_SUBDEV_FORMAT_ACTIVE )
return - EINVAL ;
/* Only CROP, CROP_DEFAULT and CROP_BOUNDS are supported */
if ( sel - > target > V4L2_SEL_TGT_CROP_BOUNDS )
return - EINVAL ;
sel - > r . left = 0 ;
sel - > r . top = 0 ;
sel - > r . width = state - > timings . bt . width ;
sel - > r . height = state - > timings . bt . height ;
return 0 ;
}
2015-03-04 12:47:54 +03:00
static int adv76xx_set_format ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
2014-01-27 01:42:37 +04:00
struct v4l2_subdev_format * format )
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_format_info * info ;
2014-01-27 01:42:37 +04:00
if ( format - > pad ! = state - > source_pad )
return - EINVAL ;
2015-02-03 20:13:18 +03:00
info = adv76xx_format_info ( state , format - > format . code ) ;
2017-08-28 13:50:28 +03:00
if ( ! info )
2015-02-03 20:13:18 +03:00
info = adv76xx_format_info ( state , MEDIA_BUS_FMT_YUYV8_2X8 ) ;
2014-01-27 01:42:37 +04:00
2015-02-03 20:13:18 +03:00
adv76xx_fill_format ( state , & format - > format ) ;
2014-01-27 01:42:37 +04:00
format - > format . code = info - > code ;
if ( format - > which = = V4L2_SUBDEV_FORMAT_TRY ) {
struct v4l2_mbus_framefmt * fmt ;
2015-03-04 12:47:54 +03:00
fmt = v4l2_subdev_get_try_format ( sd , cfg , format - > pad ) ;
2014-01-27 01:42:37 +04:00
fmt - > code = format - > format . code ;
} else {
state - > format = info ;
2015-02-03 20:13:18 +03:00
adv76xx_setup_format ( state ) ;
2014-01-27 01:42:37 +04:00
}
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-09-07 14:12:57 +03:00
# if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
static void adv76xx_cec_tx_raw_status ( struct v4l2_subdev * sd , u8 tx_raw_status )
{
struct adv76xx_state * state = to_state ( sd ) ;
if ( ( cec_read ( sd , 0x11 ) & 0x01 ) = = 0 ) {
v4l2_dbg ( 1 , debug , sd , " %s: tx raw: tx disabled \n " , __func__ ) ;
return ;
}
if ( tx_raw_status & 0x02 ) {
v4l2_dbg ( 1 , debug , sd , " %s: tx raw: arbitration lost \n " ,
__func__ ) ;
cec_transmit_done ( state - > cec_adap , CEC_TX_STATUS_ARB_LOST ,
1 , 0 , 0 , 0 ) ;
2017-12-03 18:03:11 +03:00
return ;
2015-09-07 14:12:57 +03:00
}
if ( tx_raw_status & 0x04 ) {
u8 status ;
u8 nack_cnt ;
u8 low_drive_cnt ;
v4l2_dbg ( 1 , debug , sd , " %s: tx raw: retry failed \n " , __func__ ) ;
/*
* We set this status bit since this hardware performs
* retransmissions .
*/
status = CEC_TX_STATUS_MAX_RETRIES ;
nack_cnt = cec_read ( sd , 0x14 ) & 0xf ;
if ( nack_cnt )
status | = CEC_TX_STATUS_NACK ;
low_drive_cnt = cec_read ( sd , 0x14 ) > > 4 ;
if ( low_drive_cnt )
status | = CEC_TX_STATUS_LOW_DRIVE ;
cec_transmit_done ( state - > cec_adap , status ,
0 , nack_cnt , low_drive_cnt , 0 ) ;
return ;
}
if ( tx_raw_status & 0x01 ) {
v4l2_dbg ( 1 , debug , sd , " %s: tx raw: ready ok \n " , __func__ ) ;
cec_transmit_done ( state - > cec_adap , CEC_TX_STATUS_OK , 0 , 0 , 0 , 0 ) ;
return ;
}
}
static void adv76xx_cec_isr ( struct v4l2_subdev * sd , bool * handled )
{
struct adv76xx_state * state = to_state ( sd ) ;
2018-10-12 14:30:02 +03:00
const struct adv76xx_chip_info * info = state - > info ;
2015-09-07 14:12:57 +03:00
u8 cec_irq ;
/* cec controller */
2018-10-12 14:30:02 +03:00
cec_irq = io_read ( sd , info - > cec_irq_status ) & 0x0f ;
2015-09-07 14:12:57 +03:00
if ( ! cec_irq )
return ;
v4l2_dbg ( 1 , debug , sd , " %s: cec: irq 0x%x \n " , __func__ , cec_irq ) ;
adv76xx_cec_tx_raw_status ( sd , cec_irq ) ;
if ( cec_irq & 0x08 ) {
struct cec_msg msg ;
msg . len = cec_read ( sd , 0x25 ) & 0x1f ;
if ( msg . len > 16 )
msg . len = 16 ;
if ( msg . len ) {
u8 i ;
for ( i = 0 ; i < msg . len ; i + + )
msg . msg [ i ] = cec_read ( sd , i + 0x15 ) ;
2018-10-12 14:30:02 +03:00
cec_write ( sd , info - > cec_rx_enable ,
info - > cec_rx_enable_mask ) ; /* re-enable rx */
2015-09-07 14:12:57 +03:00
cec_received_msg ( state - > cec_adap , & msg ) ;
}
}
2018-10-12 14:30:02 +03:00
if ( info - > cec_irq_swap ) {
/*
* Note : the bit order is swapped between 0x4d and 0x4e
* on adv7604
*/
cec_irq = ( ( cec_irq & 0x08 ) > > 3 ) | ( ( cec_irq & 0x04 ) > > 1 ) |
( ( cec_irq & 0x02 ) < < 1 ) | ( ( cec_irq & 0x01 ) < < 3 ) ;
}
io_write ( sd , info - > cec_irq_status + 1 , cec_irq ) ;
2015-09-07 14:12:57 +03:00
if ( handled )
* handled = true ;
}
static int adv76xx_cec_adap_enable ( struct cec_adapter * adap , bool enable )
{
2017-03-24 19:47:56 +03:00
struct adv76xx_state * state = cec_get_drvdata ( adap ) ;
2018-10-12 14:30:02 +03:00
const struct adv76xx_chip_info * info = state - > info ;
2015-09-07 14:12:57 +03:00
struct v4l2_subdev * sd = & state - > sd ;
if ( ! state - > cec_enabled_adap & & enable ) {
cec_write_clr_set ( sd , 0x2a , 0x01 , 0x01 ) ; /* power up cec */
cec_write ( sd , 0x2c , 0x01 ) ; /* cec soft reset */
cec_write_clr_set ( sd , 0x11 , 0x01 , 0 ) ; /* initially disable tx */
/* enabled irqs: */
/* tx: ready */
/* tx: arbitration lost */
/* tx: retry timeout */
/* rx: ready */
2018-10-12 14:30:02 +03:00
io_write_clr_set ( sd , info - > cec_irq_status + 3 , 0x0f , 0x0f ) ;
cec_write ( sd , info - > cec_rx_enable , info - > cec_rx_enable_mask ) ;
2015-09-07 14:12:57 +03:00
} else if ( state - > cec_enabled_adap & & ! enable ) {
/* disable cec interrupts */
2018-10-12 14:30:02 +03:00
io_write_clr_set ( sd , info - > cec_irq_status + 3 , 0x0f , 0x00 ) ;
2015-09-07 14:12:57 +03:00
/* disable address mask 1-3 */
cec_write_clr_set ( sd , 0x27 , 0x70 , 0x00 ) ;
/* power down cec section */
cec_write_clr_set ( sd , 0x2a , 0x01 , 0x00 ) ;
state - > cec_valid_addrs = 0 ;
}
state - > cec_enabled_adap = enable ;
adv76xx_s_detect_tx_5v_ctrl ( sd ) ;
return 0 ;
}
static int adv76xx_cec_adap_log_addr ( struct cec_adapter * adap , u8 addr )
{
2017-03-24 19:47:56 +03:00
struct adv76xx_state * state = cec_get_drvdata ( adap ) ;
2015-09-07 14:12:57 +03:00
struct v4l2_subdev * sd = & state - > sd ;
unsigned int i , free_idx = ADV76XX_MAX_ADDRS ;
if ( ! state - > cec_enabled_adap )
return addr = = CEC_LOG_ADDR_INVALID ? 0 : - EIO ;
if ( addr = = CEC_LOG_ADDR_INVALID ) {
cec_write_clr_set ( sd , 0x27 , 0x70 , 0 ) ;
state - > cec_valid_addrs = 0 ;
return 0 ;
}
for ( i = 0 ; i < ADV76XX_MAX_ADDRS ; i + + ) {
bool is_valid = state - > cec_valid_addrs & ( 1 < < i ) ;
if ( free_idx = = ADV76XX_MAX_ADDRS & & ! is_valid )
free_idx = i ;
if ( is_valid & & state - > cec_addr [ i ] = = addr )
return 0 ;
}
if ( i = = ADV76XX_MAX_ADDRS ) {
i = free_idx ;
if ( i = = ADV76XX_MAX_ADDRS )
return - ENXIO ;
}
state - > cec_addr [ i ] = addr ;
state - > cec_valid_addrs | = 1 < < i ;
switch ( i ) {
case 0 :
/* enable address mask 0 */
cec_write_clr_set ( sd , 0x27 , 0x10 , 0x10 ) ;
/* set address for mask 0 */
cec_write_clr_set ( sd , 0x28 , 0x0f , addr ) ;
break ;
case 1 :
/* enable address mask 1 */
cec_write_clr_set ( sd , 0x27 , 0x20 , 0x20 ) ;
/* set address for mask 1 */
cec_write_clr_set ( sd , 0x28 , 0xf0 , addr < < 4 ) ;
break ;
case 2 :
/* enable address mask 2 */
cec_write_clr_set ( sd , 0x27 , 0x40 , 0x40 ) ;
/* set address for mask 1 */
cec_write_clr_set ( sd , 0x29 , 0x0f , addr ) ;
break ;
}
return 0 ;
}
static int adv76xx_cec_adap_transmit ( struct cec_adapter * adap , u8 attempts ,
u32 signal_free_time , struct cec_msg * msg )
{
2017-03-24 19:47:56 +03:00
struct adv76xx_state * state = cec_get_drvdata ( adap ) ;
2015-09-07 14:12:57 +03:00
struct v4l2_subdev * sd = & state - > sd ;
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 .
*/
cec_write_clr_set ( sd , 0x12 , 0x70 , max ( 1 , attempts - 1 ) < < 4 ) ;
if ( len > 16 ) {
v4l2_err ( sd , " %s: len exceeded 16 (%d) \n " , __func__ , len ) ;
return - EINVAL ;
}
/* write data */
for ( i = 0 ; i < len ; i + + )
cec_write ( sd , i , msg - > msg [ i ] ) ;
/* set length (data + header) */
cec_write ( sd , 0x10 , len ) ;
/* start transmit, enable tx */
cec_write ( sd , 0x11 , 0x01 ) ;
return 0 ;
}
static const struct cec_adap_ops adv76xx_cec_adap_ops = {
. adap_enable = adv76xx_cec_adap_enable ,
. adap_log_addr = adv76xx_cec_adap_log_addr ,
. adap_transmit = adv76xx_cec_adap_transmit ,
} ;
# endif
2015-02-03 20:13:18 +03:00
static int adv76xx_isr ( struct v4l2_subdev * sd , u32 status , bool * handled )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2013-12-10 17:15:13 +04:00
const u8 irq_reg_0x43 = io_read ( sd , 0x43 ) ;
const u8 irq_reg_0x6b = io_read ( sd , 0x6b ) ;
const u8 irq_reg_0x70 = io_read ( sd , 0x70 ) ;
u8 fmt_change_digital ;
u8 fmt_change ;
u8 tx_5v ;
if ( irq_reg_0x43 )
io_write ( sd , 0x44 , irq_reg_0x43 ) ;
if ( irq_reg_0x70 )
io_write ( sd , 0x71 , irq_reg_0x70 ) ;
if ( irq_reg_0x6b )
io_write ( sd , 0x6c , irq_reg_0x6b ) ;
2012-07-18 12:45:16 +04:00
2013-12-05 17:24:05 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: " , __func__ ) ;
2012-07-18 12:45:16 +04:00
/* format change */
2013-12-10 17:15:13 +04:00
fmt_change = irq_reg_0x43 & 0x98 ;
2013-11-25 22:45:07 +04:00
fmt_change_digital = is_digital_input ( sd )
? irq_reg_0x6b & info - > fmt_change_digital_mask
: 0 ;
2013-12-05 17:26:11 +04:00
2012-07-18 12:45:16 +04:00
if ( fmt_change | | fmt_change_digital ) {
v4l2_dbg ( 1 , debug , sd ,
2013-08-14 14:58:45 +04:00
" %s: fmt_change = 0x%x, fmt_change_digital = 0x%x \n " ,
2012-07-18 12:45:16 +04:00
__func__ , fmt_change , fmt_change_digital ) ;
2013-08-14 14:58:45 +04:00
2015-06-24 19:50:30 +03:00
v4l2_subdev_notify_event ( sd , & adv76xx_ev_fmt ) ;
2013-08-14 14:58:45 +04:00
2012-07-18 12:45:16 +04:00
if ( handled )
* handled = true ;
}
2013-12-10 17:15:13 +04:00
/* HDMI/DVI mode */
if ( irq_reg_0x6b & 0x01 ) {
v4l2_dbg ( 1 , debug , sd , " %s: irq %s mode \n " , __func__ ,
( io_read ( sd , 0x6a ) & 0x01 ) ? " HDMI " : " DVI " ) ;
set_rgb_quantization_range ( sd ) ;
if ( handled )
* handled = true ;
}
2015-09-07 14:12:57 +03:00
# if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
/* cec */
adv76xx_cec_isr ( sd , handled ) ;
# endif
2012-07-18 12:45:16 +04:00
/* tx 5v detect */
2016-02-10 13:09:10 +03:00
tx_5v = irq_reg_0x70 & info - > cable_det_mask ;
2012-07-18 12:45:16 +04:00
if ( tx_5v ) {
v4l2_dbg ( 1 , debug , sd , " %s: tx_5v: 0x%x \n " , __func__ , tx_5v ) ;
2015-02-03 20:13:18 +03:00
adv76xx_s_detect_tx_5v_ctrl ( sd ) ;
2012-07-18 12:45:16 +04:00
if ( handled )
* handled = true ;
}
return 0 ;
}
2018-10-12 14:30:02 +03:00
static irqreturn_t adv76xx_irq_handler ( int irq , void * dev_id )
{
struct adv76xx_state * state = dev_id ;
bool handled = false ;
adv76xx_isr ( & state - > sd , 0 , & handled ) ;
return handled ? IRQ_HANDLED : IRQ_NONE ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_get_edid ( struct v4l2_subdev * sd , struct v4l2_edid * edid )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2013-12-10 16:45:00 +04:00
u8 * data = NULL ;
2012-07-18 12:45:16 +04:00
2014-11-07 15:34:57 +03:00
memset ( edid - > reserved , 0 , sizeof ( edid - > reserved ) ) ;
2013-12-10 16:45:00 +04:00
switch ( edid - > pad ) {
2015-02-03 20:13:18 +03:00
case ADV76XX_PAD_HDMI_PORT_A :
2014-01-29 17:08:58 +04:00
case ADV7604_PAD_HDMI_PORT_B :
case ADV7604_PAD_HDMI_PORT_C :
case ADV7604_PAD_HDMI_PORT_D :
2013-12-10 16:45:00 +04:00
if ( state - > edid . present & ( 1 < < edid - > pad ) )
data = state - > edid . edid ;
break ;
default :
return - EINVAL ;
}
2014-11-07 15:34:57 +03:00
if ( edid - > start_block = = 0 & & edid - > blocks = = 0 ) {
edid - > blocks = data ? state - > edid . blocks : 0 ;
return 0 ;
}
2017-08-28 13:50:28 +03:00
if ( ! data )
2013-12-10 16:45:00 +04:00
return - ENODATA ;
2014-11-07 15:34:57 +03:00
if ( edid - > start_block > = state - > edid . blocks )
return - EINVAL ;
if ( edid - > start_block + edid - > blocks > state - > edid . blocks )
edid - > blocks = state - > edid . blocks - edid - > start_block ;
memcpy ( edid - > edid , data + edid - > start_block * 128 , edid - > blocks * 128 ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2015-02-03 20:13:18 +03:00
static int adv76xx_set_edid ( struct v4l2_subdev * sd , struct v4l2_edid * edid )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2015-09-07 14:12:57 +03:00
unsigned int spa_loc ;
u16 pa ;
2012-07-18 12:45:16 +04:00
int err ;
2013-12-10 16:57:09 +04:00
int i ;
2012-07-18 12:45:16 +04:00
2014-11-07 15:34:57 +03:00
memset ( edid - > reserved , 0 , sizeof ( edid - > reserved ) ) ;
2014-01-29 17:08:58 +04:00
if ( edid - > pad > ADV7604_PAD_HDMI_PORT_D )
2012-07-18 12:45:16 +04:00
return - EINVAL ;
if ( edid - > start_block ! = 0 )
return - EINVAL ;
if ( edid - > blocks = = 0 ) {
2013-12-10 16:55:18 +04:00
/* Disable hotplug and I2C access to EDID RAM from DDC port */
2013-12-10 16:45:00 +04:00
state - > edid . present & = ~ ( 1 < < edid - > pad ) ;
2015-02-03 20:13:18 +03:00
adv76xx_set_hpd ( state , state - > edid . present ) ;
2014-01-31 00:17:42 +04:00
rep_write_clr_set ( sd , info - > edid_enable_reg , 0x0f , state - > edid . present ) ;
2013-12-10 16:55:18 +04:00
2012-07-18 12:45:16 +04:00
/* Fall back to a 16:9 aspect ratio */
state - > aspect_ratio . numerator = 16 ;
state - > aspect_ratio . denominator = 9 ;
2013-12-10 16:55:18 +04:00
2018-10-04 10:57:06 +03:00
if ( ! state - > edid . present ) {
2013-12-10 16:55:18 +04:00
state - > edid . blocks = 0 ;
2018-10-04 10:57:06 +03:00
cec_phys_addr_invalidate ( state - > cec_adap ) ;
}
2013-12-10 16:55:18 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: clear EDID pad %d, edid.present = 0x%x \n " ,
__func__ , edid - > pad , state - > edid . present ) ;
2012-07-18 12:45:16 +04:00
return 0 ;
}
2013-12-10 16:45:00 +04:00
if ( edid - > blocks > 2 ) {
edid - > blocks = 2 ;
2012-07-18 12:45:16 +04:00
return - E2BIG ;
2013-12-10 16:45:00 +04:00
}
2018-09-13 10:40:56 +03:00
pa = v4l2_get_edid_phys_addr ( edid - > edid , edid - > blocks * 128 , & spa_loc ) ;
err = v4l2_phys_addr_validate ( pa , & pa , NULL ) ;
2015-09-07 14:12:57 +03:00
if ( err )
return err ;
2013-12-10 16:45:00 +04:00
2013-12-10 16:57:09 +04:00
v4l2_dbg ( 2 , debug , sd , " %s: write EDID pad %d, edid.present = 0x%x \n " ,
__func__ , edid - > pad , state - > edid . present ) ;
2013-12-10 16:55:18 +04:00
/* Disable hotplug and I2C access to EDID RAM from DDC port */
2013-12-10 16:45:00 +04:00
cancel_delayed_work_sync ( & state - > delayed_work_enable_hotplug ) ;
2015-02-03 20:13:18 +03:00
adv76xx_set_hpd ( state , 0 ) ;
2014-01-31 00:17:42 +04:00
rep_write_clr_set ( sd , info - > edid_enable_reg , 0x0f , 0x00 ) ;
2013-12-10 16:55:18 +04:00
2015-09-07 14:12:57 +03:00
/*
* Return an error if no location of the source physical address
* was found .
*/
if ( spa_loc = = 0 )
return - EINVAL ;
2013-12-10 16:57:09 +04:00
2013-12-10 16:55:18 +04:00
switch ( edid - > pad ) {
2015-02-03 20:13:18 +03:00
case ADV76XX_PAD_HDMI_PORT_A :
2013-12-10 16:57:09 +04:00
state - > spa_port_a [ 0 ] = edid - > edid [ spa_loc ] ;
state - > spa_port_a [ 1 ] = edid - > edid [ spa_loc + 1 ] ;
2013-12-10 16:55:18 +04:00
break ;
2014-01-29 17:08:58 +04:00
case ADV7604_PAD_HDMI_PORT_B :
2013-12-10 16:57:09 +04:00
rep_write ( sd , 0x70 , edid - > edid [ spa_loc ] ) ;
rep_write ( sd , 0x71 , edid - > edid [ spa_loc + 1 ] ) ;
2013-12-10 16:55:18 +04:00
break ;
2014-01-29 17:08:58 +04:00
case ADV7604_PAD_HDMI_PORT_C :
2013-12-10 16:57:09 +04:00
rep_write ( sd , 0x72 , edid - > edid [ spa_loc ] ) ;
rep_write ( sd , 0x73 , edid - > edid [ spa_loc + 1 ] ) ;
2013-12-10 16:55:18 +04:00
break ;
2014-01-29 17:08:58 +04:00
case ADV7604_PAD_HDMI_PORT_D :
2013-12-10 16:57:09 +04:00
rep_write ( sd , 0x74 , edid - > edid [ spa_loc ] ) ;
rep_write ( sd , 0x75 , edid - > edid [ spa_loc + 1 ] ) ;
2013-12-10 16:55:18 +04:00
break ;
2013-12-10 16:57:09 +04:00
default :
return - EINVAL ;
2013-12-10 16:55:18 +04:00
}
2013-11-25 22:45:07 +04:00
if ( info - > type = = ADV7604 ) {
rep_write ( sd , 0x76 , spa_loc & 0xff ) ;
2014-01-31 00:17:42 +04:00
rep_write_clr_set ( sd , 0x77 , 0x40 , ( spa_loc & 0x100 ) > > 2 ) ;
2013-11-25 22:45:07 +04:00
} else {
2016-02-17 17:57:56 +03:00
/* ADV7612 Software Manual Rev. A, p. 15 */
rep_write ( sd , 0x70 , spa_loc & 0xff ) ;
2014-01-31 00:17:42 +04:00
rep_write_clr_set ( sd , 0x71 , 0x01 , ( spa_loc & 0x100 ) > > 8 ) ;
2013-11-25 22:45:07 +04:00
}
2013-12-10 16:55:18 +04:00
2013-12-10 16:57:09 +04:00
edid - > edid [ spa_loc ] = state - > spa_port_a [ 0 ] ;
edid - > edid [ spa_loc + 1 ] = state - > spa_port_a [ 1 ] ;
2013-12-10 16:45:00 +04:00
memcpy ( state - > edid . edid , edid - > edid , 128 * edid - > blocks ) ;
state - > edid . blocks = edid - > blocks ;
2012-07-18 12:45:16 +04:00
state - > aspect_ratio = v4l2_calc_aspect_ratio ( edid - > edid [ 0x15 ] ,
edid - > edid [ 0x16 ] ) ;
2013-12-10 16:55:18 +04:00
state - > edid . present | = 1 < < edid - > pad ;
2013-12-10 16:45:00 +04:00
err = edid_write_block ( sd , 128 * edid - > blocks , state - > edid . edid ) ;
if ( err < 0 ) {
2013-12-10 16:55:18 +04:00
v4l2_err ( sd , " error %d writing edid pad %d \n " , err , edid - > pad ) ;
2013-12-10 16:45:00 +04:00
return err ;
}
2015-02-03 20:13:18 +03:00
/* adv76xx calculates the checksums and enables I2C access to internal
2013-12-10 16:57:09 +04:00
EDID RAM from DDC port . */
2014-01-31 00:17:42 +04:00
rep_write_clr_set ( sd , info - > edid_enable_reg , 0x0f , state - > edid . present ) ;
2013-12-10 16:57:09 +04:00
for ( i = 0 ; i < 1000 ; i + + ) {
2013-11-25 22:45:07 +04:00
if ( rep_read ( sd , info - > edid_status_reg ) & state - > edid . present )
2013-12-10 16:57:09 +04:00
break ;
mdelay ( 1 ) ;
}
if ( i = = 1000 ) {
v4l2_err ( sd , " error enabling edid (0x%x) \n " , state - > edid . present ) ;
return - EIO ;
}
2015-09-07 14:12:57 +03:00
cec_s_phys_addr ( state - > cec_adap , pa , false ) ;
2013-12-10 16:57:09 +04:00
2013-12-10 16:45:00 +04:00
/* enable hotplug after 100 ms */
2016-07-02 13:43:55 +03:00
schedule_delayed_work ( & state - > delayed_work_enable_hotplug , HZ / 10 ) ;
2013-12-10 16:45:00 +04:00
return 0 ;
2012-07-18 12:45:16 +04:00
}
/*********** avi info frame CEA-861-E **************/
2015-06-07 13:32:33 +03:00
static const struct adv76xx_cfg_read_infoframe adv76xx_cri [ ] = {
{ " AVI " , 0x01 , 0xe0 , 0x00 } ,
{ " Audio " , 0x02 , 0xe3 , 0x1c } ,
{ " SDP " , 0x04 , 0xe6 , 0x2a } ,
{ " Vendor " , 0x10 , 0xec , 0x54 }
} ;
static int adv76xx_read_infoframe ( struct v4l2_subdev * sd , int index ,
union hdmi_infoframe * frame )
2012-07-18 12:45:16 +04:00
{
2015-06-07 13:32:33 +03:00
uint8_t buffer [ 32 ] ;
u8 len ;
2012-07-18 12:45:16 +04:00
int i ;
2015-06-07 13:32:33 +03:00
if ( ! ( io_read ( sd , 0x60 ) & adv76xx_cri [ index ] . present_mask ) ) {
v4l2_info ( sd , " %s infoframe not received \n " ,
adv76xx_cri [ index ] . desc ) ;
return - ENOENT ;
2012-07-18 12:45:16 +04:00
}
2015-06-07 13:32:33 +03:00
for ( i = 0 ; i < 3 ; i + + )
buffer [ i ] = infoframe_read ( sd ,
adv76xx_cri [ index ] . head_addr + i ) ;
len = buffer [ 2 ] + 1 ;
if ( len + 3 > sizeof ( buffer ) ) {
v4l2_err ( sd , " %s: invalid %s infoframe length %d \n " , __func__ ,
adv76xx_cri [ index ] . desc , len ) ;
return - ENOENT ;
2012-07-18 12:45:16 +04:00
}
2015-06-07 13:32:33 +03:00
for ( i = 0 ; i < len ; i + + )
buffer [ i + 3 ] = infoframe_read ( sd ,
adv76xx_cri [ index ] . payload_addr + i ) ;
2018-09-20 21:51:29 +03:00
if ( hdmi_infoframe_unpack ( frame , buffer , sizeof ( buffer ) ) < 0 ) {
2015-06-07 13:32:33 +03:00
v4l2_err ( sd , " %s: unpack of %s infoframe failed \n " , __func__ ,
adv76xx_cri [ index ] . desc ) ;
return - ENOENT ;
2012-07-18 12:45:16 +04:00
}
2015-06-07 13:32:33 +03:00
return 0 ;
}
2012-07-18 12:45:16 +04:00
2015-06-07 13:32:33 +03:00
static void adv76xx_log_infoframes ( struct v4l2_subdev * sd )
{
int i ;
2012-07-18 12:45:16 +04:00
2015-06-07 13:32:33 +03:00
if ( ! is_hdmi ( sd ) ) {
v4l2_info ( sd , " receive DVI-D signal, no infoframes \n " ) ;
2012-07-18 12:45:16 +04:00
return ;
2015-06-07 13:32:33 +03:00
}
2012-07-18 12:45:16 +04:00
2015-06-07 13:32:33 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( adv76xx_cri ) ; i + + ) {
union hdmi_infoframe frame ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-06-07 13:32:33 +03:00
if ( adv76xx_read_infoframe ( sd , i , & frame ) )
return ;
hdmi_infoframe_log ( KERN_INFO , & client - > dev , & frame ) ;
}
2012-07-18 12:45:16 +04:00
}
2015-02-03 20:13:18 +03:00
static int adv76xx_log_status ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
2012-07-18 12:45:16 +04:00
struct v4l2_dv_timings timings ;
struct stdi_readback stdi ;
u8 reg_io_0x02 = io_read ( sd , 0x02 ) ;
2014-01-09 03:26:55 +04:00
u8 edid_enabled ;
u8 cable_det ;
2012-07-18 12:45:16 +04:00
2013-11-25 23:15:29 +04:00
static const char * const csc_coeff_sel_rb [ 16 ] = {
2012-07-18 12:45:16 +04:00
" bypassed " , " YPbPr601 -> RGB " , " reserved " , " YPbPr709 -> RGB " ,
" reserved " , " RGB -> YPbPr601 " , " reserved " , " RGB -> YPbPr709 " ,
" reserved " , " YPbPr709 -> YPbPr601 " , " YPbPr601 -> YPbPr709 " ,
" reserved " , " reserved " , " reserved " , " reserved " , " manual "
} ;
2013-11-25 23:15:29 +04:00
static const char * const input_color_space_txt [ 16 ] = {
2012-07-18 12:45:16 +04:00
" RGB limited range (16-235) " , " RGB full range (0-255) " ,
" YCbCr Bt.601 (16-235) " , " YCbCr Bt.709 (16-235) " ,
2013-12-05 17:05:58 +04:00
" xvYCC Bt.601 " , " xvYCC Bt.709 " ,
2012-07-18 12:45:16 +04:00
" YCbCr Bt.601 (0-255) " , " YCbCr Bt.709 (0-255) " ,
" invalid " , " invalid " , " invalid " , " invalid " , " invalid " ,
" invalid " , " invalid " , " automatic "
} ;
2015-06-07 13:32:35 +03:00
static const char * const hdmi_color_space_txt [ 16 ] = {
" RGB limited range (16-235) " , " RGB full range (0-255) " ,
" YCbCr Bt.601 (16-235) " , " YCbCr Bt.709 (16-235) " ,
" xvYCC Bt.601 " , " xvYCC Bt.709 " ,
" YCbCr Bt.601 (0-255) " , " YCbCr Bt.709 (0-255) " ,
2018-09-14 11:58:03 +03:00
" sYCC " , " opYCC 601 " , " opRGB " , " invalid " , " invalid " ,
2015-06-07 13:32:35 +03:00
" invalid " , " invalid " , " invalid "
} ;
2013-11-25 23:15:29 +04:00
static const char * const rgb_quantization_range_txt [ ] = {
2012-07-18 12:45:16 +04:00
" Automatic " ,
" RGB limited range (16-235) " ,
" RGB full range (0-255) " ,
} ;
2013-11-25 23:15:29 +04:00
static const char * const deep_color_mode_txt [ 4 ] = {
2013-08-14 15:52:46 +04:00
" 8-bits per channel " ,
" 10-bits per channel " ,
" 12-bits per channel " ,
" 16-bits per channel (not supported) "
} ;
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " -----Chip status----- \n " ) ;
v4l2_info ( sd , " Chip power: %s \n " , no_power ( sd ) ? " off " : " on " ) ;
2013-11-25 22:45:07 +04:00
edid_enabled = rep_read ( sd , info - > edid_status_reg ) ;
2013-12-10 16:45:00 +04:00
v4l2_info ( sd , " EDID enabled port A: %s, B: %s, C: %s, D: %s \n " ,
2014-01-09 03:26:55 +04:00
( ( edid_enabled & 0x01 ) ? " Yes " : " No " ) ,
( ( edid_enabled & 0x02 ) ? " Yes " : " No " ) ,
( ( edid_enabled & 0x04 ) ? " Yes " : " No " ) ,
( ( edid_enabled & 0x08 ) ? " Yes " : " No " ) ) ;
2015-09-07 14:12:57 +03:00
v4l2_info ( sd , " CEC: %s \n " , state - > cec_enabled_adap ?
2012-07-18 12:45:16 +04:00
" enabled " : " disabled " ) ;
2015-09-07 14:12:57 +03:00
if ( state - > cec_enabled_adap ) {
int i ;
for ( i = 0 ; i < ADV76XX_MAX_ADDRS ; i + + ) {
bool is_valid = state - > cec_valid_addrs & ( 1 < < i ) ;
if ( is_valid )
v4l2_info ( sd , " CEC Logical Address: 0x%x \n " ,
state - > cec_addr [ i ] ) ;
}
}
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " -----Signal status----- \n " ) ;
2013-11-25 22:45:07 +04:00
cable_det = info - > read_cable_det ( sd ) ;
2013-12-10 16:45:00 +04:00
v4l2_info ( sd , " Cable detected (+5V power) port A: %s, B: %s, C: %s, D: %s \n " ,
2013-11-25 22:45:07 +04:00
( ( cable_det & 0x01 ) ? " Yes " : " No " ) ,
( ( cable_det & 0x02 ) ? " Yes " : " No " ) ,
2014-01-09 03:26:55 +04:00
( ( cable_det & 0x04 ) ? " Yes " : " No " ) ,
2013-11-25 22:45:07 +04:00
( ( cable_det & 0x08 ) ? " Yes " : " No " ) ) ;
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " TMDS signal detected: %s \n " ,
no_signal_tmds ( sd ) ? " false " : " true " ) ;
v4l2_info ( sd , " TMDS signal locked: %s \n " ,
no_lock_tmds ( sd ) ? " false " : " true " ) ;
v4l2_info ( sd , " SSPD locked: %s \n " , no_lock_sspd ( sd ) ? " false " : " true " ) ;
v4l2_info ( sd , " STDI locked: %s \n " , no_lock_stdi ( sd ) ? " false " : " true " ) ;
v4l2_info ( sd , " CP locked: %s \n " , no_lock_cp ( sd ) ? " false " : " true " ) ;
v4l2_info ( sd , " CP free run: %s \n " ,
2015-02-06 17:37:58 +03:00
( in_free_run ( sd ) ) ? " on " : " off " ) ;
2012-10-16 17:02:05 +04:00
v4l2_info ( sd , " Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x \n " ,
io_read ( sd , 0x01 ) & 0x0f , io_read ( sd , 0x00 ) & 0x3f ,
( io_read ( sd , 0x01 ) & 0x70 ) > > 4 ) ;
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " -----Video Timings----- \n " ) ;
if ( read_stdi ( sd , & stdi ) )
v4l2_info ( sd , " STDI: not locked \n " ) ;
else
v4l2_info ( sd , " STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %s, %chsync, %cvsync \n " ,
stdi . lcf , stdi . bl , stdi . lcvs ,
stdi . interlaced ? " interlaced " : " progressive " ,
stdi . hs_pol , stdi . vs_pol ) ;
2015-02-03 20:13:18 +03:00
if ( adv76xx_query_dv_timings ( sd , & timings ) )
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " No video detected \n " ) ;
else
2013-08-15 15:05:59 +04:00
v4l2_print_dv_timings ( sd - > name , " Detected format: " ,
& timings , true ) ;
v4l2_print_dv_timings ( sd - > name , " Configured format: " ,
& state - > timings , true ) ;
2012-07-18 12:45:16 +04:00
2013-08-14 15:56:57 +04:00
if ( no_signal ( sd ) )
return 0 ;
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " -----Color space----- \n " ) ;
v4l2_info ( sd , " RGB quantization range ctrl: %s \n " ,
rgb_quantization_range_txt [ state - > rgb_quantization_range ] ) ;
v4l2_info ( sd , " Input color space: %s \n " ,
input_color_space_txt [ reg_io_0x02 > > 4 ] ) ;
2016-06-28 17:43:01 +03:00
v4l2_info ( sd , " Output color space: %s %s, alt-gamma %s \n " ,
2012-07-18 12:45:16 +04:00
( reg_io_0x02 & 0x02 ) ? " RGB " : " YCbCr " ,
2015-06-07 13:32:34 +03:00
( ( ( reg_io_0x02 > > 2 ) & 0x01 ) ^ ( reg_io_0x02 & 0x01 ) ) ?
2016-06-28 17:43:01 +03:00
" (16-235) " : " (0-255) " ,
2015-06-07 13:32:35 +03:00
( reg_io_0x02 & 0x08 ) ? " enabled " : " disabled " ) ;
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " Color space conversion: %s \n " ,
2015-02-04 17:16:00 +03:00
csc_coeff_sel_rb [ cp_read ( sd , info - > cp_csc ) > > 4 ] ) ;
2012-07-18 12:45:16 +04:00
2013-12-10 16:45:00 +04:00
if ( ! is_digital_input ( sd ) )
2013-08-14 15:56:57 +04:00
return 0 ;
v4l2_info ( sd , " -----%s status----- \n " , is_hdmi ( sd ) ? " HDMI " : " DVI-D " ) ;
2013-12-10 16:45:00 +04:00
v4l2_info ( sd , " Digital video port selected: %c \n " ,
( hdmi_read ( sd , 0x00 ) & 0x03 ) + ' A ' ) ;
v4l2_info ( sd , " HDCP encrypted content: %s \n " ,
( hdmi_read ( sd , 0x05 ) & 0x40 ) ? " true " : " false " ) ;
2013-08-14 15:56:57 +04:00
v4l2_info ( sd , " HDCP keys read: %s%s \n " ,
( hdmi_read ( sd , 0x04 ) & 0x20 ) ? " yes " : " no " ,
( hdmi_read ( sd , 0x04 ) & 0x10 ) ? " ERROR " : " " ) ;
2014-09-12 13:02:02 +04:00
if ( is_hdmi ( sd ) ) {
2013-08-14 15:56:57 +04:00
bool audio_pll_locked = hdmi_read ( sd , 0x04 ) & 0x01 ;
bool audio_sample_packet_detect = hdmi_read ( sd , 0x18 ) & 0x01 ;
bool audio_mute = io_read ( sd , 0x65 ) & 0x40 ;
v4l2_info ( sd , " Audio: pll %s, samples %s, %s \n " ,
audio_pll_locked ? " locked " : " not locked " ,
audio_sample_packet_detect ? " detected " : " not detected " ,
audio_mute ? " muted " : " enabled " ) ;
if ( audio_pll_locked & & audio_sample_packet_detect ) {
v4l2_info ( sd , " Audio format: %s \n " ,
( hdmi_read ( sd , 0x07 ) & 0x20 ) ? " multi-channel " : " stereo " ) ;
}
v4l2_info ( sd , " Audio CTS: %u \n " , ( hdmi_read ( sd , 0x5b ) < < 12 ) +
( hdmi_read ( sd , 0x5c ) < < 8 ) +
( hdmi_read ( sd , 0x5d ) & 0xf0 ) ) ;
v4l2_info ( sd , " Audio N: %u \n " , ( ( hdmi_read ( sd , 0x5d ) & 0x0f ) < < 16 ) +
( hdmi_read ( sd , 0x5e ) < < 8 ) +
hdmi_read ( sd , 0x5f ) ) ;
v4l2_info ( sd , " AV Mute: %s \n " , ( hdmi_read ( sd , 0x04 ) & 0x40 ) ? " on " : " off " ) ;
v4l2_info ( sd , " Deep color mode: %s \n " , deep_color_mode_txt [ ( hdmi_read ( sd , 0x0b ) & 0x60 ) > > 5 ] ) ;
2015-06-07 13:32:35 +03:00
v4l2_info ( sd , " HDMI colorspace: %s \n " , hdmi_color_space_txt [ hdmi_read ( sd , 0x53 ) & 0xf ] ) ;
2013-08-14 15:56:57 +04:00
2015-06-07 13:32:33 +03:00
adv76xx_log_infoframes ( sd ) ;
2012-07-18 12:45:16 +04:00
}
return 0 ;
}
2015-06-24 19:50:30 +03:00
static int adv76xx_subscribe_event ( struct v4l2_subdev * sd ,
struct v4l2_fh * fh ,
struct v4l2_event_subscription * sub )
{
switch ( sub - > type ) {
case V4L2_EVENT_SOURCE_CHANGE :
return v4l2_src_change_event_subdev_subscribe ( sd , fh , sub ) ;
case V4L2_EVENT_CTRL :
return v4l2_ctrl_subdev_subscribe_event ( sd , fh , sub ) ;
default :
return - EINVAL ;
}
}
2015-09-07 14:12:57 +03:00
static int adv76xx_registered ( struct v4l2_subdev * sd )
{
struct adv76xx_state * state = to_state ( sd ) ;
2016-11-25 11:23:34 +03:00
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2015-09-07 14:12:57 +03:00
int err ;
2016-11-25 11:23:34 +03:00
err = cec_register_adapter ( state - > cec_adap , & client - > dev ) ;
2015-09-07 14:12:57 +03:00
if ( err )
cec_delete_adapter ( state - > cec_adap ) ;
return err ;
}
static void adv76xx_unregistered ( struct v4l2_subdev * sd )
{
struct adv76xx_state * state = to_state ( sd ) ;
cec_unregister_adapter ( state - > cec_adap ) ;
}
2012-07-18 12:45:16 +04:00
/* ----------------------------------------------------------------------- */
2015-02-03 20:13:18 +03:00
static const struct v4l2_ctrl_ops adv76xx_ctrl_ops = {
. s_ctrl = adv76xx_s_ctrl ,
2016-01-27 16:31:41 +03:00
. g_volatile_ctrl = adv76xx_g_volatile_ctrl ,
2012-07-18 12:45:16 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_subdev_core_ops adv76xx_core_ops = {
. log_status = adv76xx_log_status ,
. interrupt_service_routine = adv76xx_isr ,
2015-06-24 19:50:30 +03:00
. subscribe_event = adv76xx_subscribe_event ,
2015-06-24 19:50:27 +03:00
. unsubscribe_event = v4l2_event_subdev_unsubscribe ,
2012-07-18 12:45:16 +04:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
2015-02-03 20:13:18 +03:00
. g_register = adv76xx_g_register ,
. s_register = adv76xx_s_register ,
2012-07-18 12:45:16 +04:00
# endif
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
. s_routing = adv76xx_s_routing ,
. g_input_status = adv76xx_g_input_status ,
. s_dv_timings = adv76xx_s_dv_timings ,
. g_dv_timings = adv76xx_g_dv_timings ,
. query_dv_timings = adv76xx_query_dv_timings ,
2012-07-18 12:45:16 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
. enum_mbus_code = adv76xx_enum_mbus_code ,
2015-12-22 17:22:01 +03:00
. get_selection = adv76xx_get_selection ,
2015-02-03 20:13:18 +03:00
. get_fmt = adv76xx_get_format ,
. set_fmt = adv76xx_set_format ,
. get_edid = adv76xx_get_edid ,
. set_edid = adv76xx_set_edid ,
. dv_timings_cap = adv76xx_dv_timings_cap ,
. enum_dv_timings = adv76xx_enum_dv_timings ,
2012-07-18 12:45:16 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_subdev_ops adv76xx_ops = {
. core = & adv76xx_core_ops ,
. video = & adv76xx_video_ops ,
. pad = & adv76xx_pad_ops ,
2012-07-18 12:45:16 +04:00
} ;
2015-09-07 14:12:57 +03:00
static const struct v4l2_subdev_internal_ops adv76xx_int_ops = {
. registered = adv76xx_registered ,
. unregistered = adv76xx_unregistered ,
} ;
2012-07-18 12:45:16 +04:00
/* -------------------------- custom ctrls ---------------------------------- */
static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
2015-02-03 20:13:18 +03:00
. ops = & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
. id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE ,
. name = " Analog Sampling Phase " ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. min = 0 ,
. max = 0x1f ,
. step = 1 ,
. def = 0 ,
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color_manual = {
. ops = & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
. id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL ,
. name = " Free Running Color, Manual " ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. min = false ,
. max = true ,
. step = 1 ,
. def = false ,
} ;
2015-02-03 20:13:18 +03:00
static const struct v4l2_ctrl_config adv76xx_ctrl_free_run_color = {
. ops = & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
. id = V4L2_CID_ADV_RX_FREE_RUN_COLOR ,
. name = " Free Running Color " ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. min = 0x0 ,
. max = 0xffffff ,
. step = 0x1 ,
. def = 0x0 ,
} ;
/* ----------------------------------------------------------------------- */
2018-02-13 20:48:56 +03:00
struct adv76xx_register_map {
const char * name ;
u8 default_addr ;
} ;
static const struct adv76xx_register_map adv76xx_default_addresses [ ] = {
[ ADV76XX_PAGE_IO ] = { " main " , 0x4c } ,
[ ADV7604_PAGE_AVLINK ] = { " avlink " , 0x42 } ,
[ ADV76XX_PAGE_CEC ] = { " cec " , 0x40 } ,
[ ADV76XX_PAGE_INFOFRAME ] = { " infoframe " , 0x3e } ,
[ ADV7604_PAGE_ESDP ] = { " esdp " , 0x38 } ,
[ ADV7604_PAGE_DPP ] = { " dpp " , 0x3c } ,
[ ADV76XX_PAGE_AFE ] = { " afe " , 0x26 } ,
[ ADV76XX_PAGE_REP ] = { " rep " , 0x32 } ,
[ ADV76XX_PAGE_EDID ] = { " edid " , 0x36 } ,
[ ADV76XX_PAGE_HDMI ] = { " hdmi " , 0x34 } ,
[ ADV76XX_PAGE_TEST ] = { " test " , 0x30 } ,
[ ADV76XX_PAGE_CP ] = { " cp " , 0x22 } ,
[ ADV7604_PAGE_VDP ] = { " vdp " , 0x24 } ,
} ;
2015-02-03 20:13:18 +03:00
static int adv76xx_core_init ( struct v4l2_subdev * sd )
2012-07-18 12:45:16 +04:00
{
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
const struct adv76xx_chip_info * info = state - > info ;
struct adv76xx_platform_data * pdata = & state - > pdata ;
2012-07-18 12:45:16 +04:00
hdmi_write ( sd , 0x48 ,
( pdata - > disable_pwrdnb ? 0x80 : 0 ) |
( pdata - > disable_cable_det_rst ? 0x40 : 0 ) ) ;
disable_input ( sd ) ;
2014-01-31 17:57:27 +04:00
if ( pdata - > default_input > = 0 & &
pdata - > default_input < state - > source_pad ) {
state - > selected_input = pdata - > default_input ;
select_input ( sd ) ;
enable_input ( sd ) ;
}
2012-07-18 12:45:16 +04:00
/* power */
io_write ( sd , 0x0c , 0x42 ) ; /* Power up part and power down VDP */
io_write ( sd , 0x0b , 0x44 ) ; /* Power down ESDP block */
cp_write ( sd , 0xcf , 0x01 ) ; /* Power down macrovision */
/* video format */
2016-06-28 17:43:01 +03:00
io_write_clr_set ( sd , 0x02 , 0x0f , pdata - > alt_gamma < < 3 ) ;
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x05 , 0x0e , pdata - > blank_data < < 3 |
2014-01-27 01:42:37 +04:00
pdata - > insert_av_codes < < 2 |
pdata - > replicate_av_codes < < 1 ) ;
2015-02-03 20:13:18 +03:00
adv76xx_setup_format ( state ) ;
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0x69 , 0x30 ) ; /* Enable CP CSC */
2013-12-20 12:14:57 +04:00
/* VS, HS polarities */
2014-02-05 02:57:56 +04:00
io_write ( sd , 0x06 , 0xa0 | pdata - > inv_vs_pol < < 2 |
pdata - > inv_hs_pol < < 1 | pdata - > inv_llc_pol ) ;
2013-12-20 12:12:00 +04:00
/* Adjust drive strength */
io_write ( sd , 0x14 , 0x40 | pdata - > dr_str_data < < 4 |
pdata - > dr_str_clk < < 2 |
pdata - > dr_str_sync ) ;
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0xba , ( pdata - > hdmi_free_run_mode < < 1 ) | 0x01 ) ; /* HDMI free run */
cp_write ( sd , 0xf3 , 0xdc ) ; /* Low threshold to enter/exit free run mode */
cp_write ( sd , 0xf9 , 0x23 ) ; /* STDI ch. 1 - LCVS change threshold -
2012-10-16 12:46:21 +04:00
ADI recommended setting [ REF_01 , c . 2.3 .3 ] */
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0x45 , 0x23 ) ; /* STDI ch. 2 - LCVS change threshold -
2012-10-16 12:46:21 +04:00
ADI recommended setting [ REF_01 , c . 2.3 .3 ] */
2012-07-18 12:45:16 +04:00
cp_write ( sd , 0xc9 , 0x2d ) ; /* use prim_mode and vid_std as free run resolution
for digital formats */
2013-12-05 17:33:41 +04:00
/* HDMI audio */
2014-01-31 00:17:42 +04:00
hdmi_write_clr_set ( sd , 0x15 , 0x03 , 0x03 ) ; /* Mute on FIFO over-/underflow [REF_01, c. 1.2.18] */
hdmi_write_clr_set ( sd , 0x1a , 0x0e , 0x08 ) ; /* Wait 1 s before unmute */
hdmi_write_clr_set ( sd , 0x68 , 0x06 , 0x06 ) ; /* FIFO reset on over-/underflow [REF_01, c. 1.2.19] */
2013-12-05 17:33:41 +04:00
2012-07-18 12:45:16 +04:00
/* TODO from platform data */
afe_write ( sd , 0xb5 , 0x01 ) ; /* Setting MCLK to 256Fs */
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) ) {
2013-11-25 22:45:07 +04:00
afe_write ( sd , 0x02 , pdata - > ain_sel ) ; /* Select analog input muxing mode */
2014-01-31 00:17:42 +04:00
io_write_clr_set ( sd , 0x30 , 1 < < 4 , pdata - > output_bus_lsb_to_msb < < 4 ) ;
2013-11-25 22:45:07 +04:00
}
2012-07-18 12:45:16 +04:00
/* interrupts */
2013-11-25 22:45:07 +04:00
io_write ( sd , 0x40 , 0xc0 | pdata - > int1_config ) ; /* Configure INT1 */
2012-07-18 12:45:16 +04:00
io_write ( sd , 0x46 , 0x98 ) ; /* Enable SSPD, STDI and CP unlocked interrupts */
2013-11-25 22:45:07 +04:00
io_write ( sd , 0x6e , info - > fmt_change_digital_mask ) ; /* Enable V_LOCKED and DE_REGEN_LCK interrupts */
io_write ( sd , 0x73 , info - > cable_det_mask ) ; /* Enable cable detection (+5v) interrupts */
info - > setup_irqs ( sd ) ;
2012-07-18 12:45:16 +04:00
return v4l2_ctrl_handler_setup ( sd - > ctrl_handler ) ;
}
2013-11-25 22:45:07 +04:00
static void adv7604_setup_irqs ( struct v4l2_subdev * sd )
{
io_write ( sd , 0x41 , 0xd7 ) ; /* STDI irq for any change, disable INT2 */
}
static void adv7611_setup_irqs ( struct v4l2_subdev * sd )
{
io_write ( sd , 0x41 , 0xd0 ) ; /* STDI irq for any change, disable INT2 */
}
2015-06-03 16:59:51 +03:00
static void adv7612_setup_irqs ( struct v4l2_subdev * sd )
{
io_write ( sd , 0x41 , 0xd0 ) ; /* disable INT2 */
}
2015-02-03 20:13:18 +03:00
static void adv76xx_unregister_clients ( struct adv76xx_state * state )
2012-07-18 12:45:16 +04:00
{
2014-01-30 23:32:21 +04:00
unsigned int i ;
2019-08-09 18:40:47 +03:00
for ( i = 1 ; i < ARRAY_SIZE ( state - > i2c_clients ) ; + + i )
i2c_unregister_device ( state - > i2c_clients [ i ] ) ;
2012-07-18 12:45:16 +04:00
}
2015-02-03 20:13:18 +03:00
static struct i2c_client * adv76xx_dummy_client ( struct v4l2_subdev * sd ,
2018-02-13 20:48:56 +03:00
unsigned int page )
2012-07-18 12:45:16 +04:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2018-02-13 20:48:56 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
struct adv76xx_platform_data * pdata = & state - > pdata ;
unsigned int io_reg = 0xf2 + page ;
struct i2c_client * new_client ;
if ( pdata & & pdata - > i2c_addresses [ page ] )
2019-08-09 18:40:47 +03:00
new_client = i2c_new_dummy_device ( client - > adapter ,
2018-02-13 20:48:56 +03:00
pdata - > i2c_addresses [ page ] ) ;
else
2019-08-09 18:40:47 +03:00
new_client = i2c_new_ancillary_device ( client ,
2018-02-13 20:48:56 +03:00
adv76xx_default_addresses [ page ] . name ,
adv76xx_default_addresses [ page ] . default_addr ) ;
2012-07-18 12:45:16 +04:00
2019-08-09 18:40:47 +03:00
if ( ! IS_ERR ( new_client ) )
2018-02-13 20:48:56 +03:00
io_write ( sd , io_reg , new_client - > addr < < 1 ) ;
return new_client ;
2012-07-18 12:45:16 +04:00
}
2015-02-03 20:13:18 +03:00
static const struct adv76xx_reg_seq adv7604_recommended_settings_afe [ ] = {
2013-11-25 22:45:07 +04:00
/* reset ADI recommended settings for HDMI: */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x0d ) , 0x04 } , /* HDMI filter optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x0d ) , 0x04 } , /* HDMI filter optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x3d ) , 0x00 } , /* DDC bus active pull-up control */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x3e ) , 0x74 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x4e ) , 0x3b } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x57 ) , 0x74 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x58 ) , 0x63 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8d ) , 0x18 } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8e ) , 0x34 } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x93 ) , 0x88 } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x94 ) , 0x2e } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x96 ) , 0x00 } , /* enable automatic EQ changing */
2013-11-25 22:45:07 +04:00
/* set ADI recommended settings for digitizer */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG ( ADV76XX_PAGE_AFE , 0x12 ) , 0x7b } , /* ADC noise shaping filter controls */
{ ADV76XX_REG ( ADV76XX_PAGE_AFE , 0x0c ) , 0x1f } , /* CP core gain controls */
{ ADV76XX_REG ( ADV76XX_PAGE_CP , 0x3e ) , 0x04 } , /* CP core pre-gain control */
{ ADV76XX_REG ( ADV76XX_PAGE_CP , 0xc3 ) , 0x39 } , /* CP coast control. Graphics mode */
{ ADV76XX_REG ( ADV76XX_PAGE_CP , 0x40 ) , 0x5c } , /* CP core pre-gain control. Graphics mode */
2013-11-25 22:45:07 +04:00
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG_SEQ_TERM , 0 } ,
2013-11-25 22:45:07 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct adv76xx_reg_seq adv7604_recommended_settings_hdmi [ ] = {
2013-11-25 22:45:07 +04:00
/* set ADI recommended settings for HDMI: */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x0d ) , 0x84 } , /* HDMI filter optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x3d ) , 0x10 } , /* DDC bus active pull-up control */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x3e ) , 0x39 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x4e ) , 0x3b } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x57 ) , 0xb6 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x58 ) , 0x03 } , /* TMDS PLL optimization */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8d ) , 0x18 } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8e ) , 0x34 } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x93 ) , 0x8b } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x94 ) , 0x2d } , /* equaliser */
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x96 ) , 0x01 } , /* enable automatic EQ changing */
2013-11-25 22:45:07 +04:00
/* reset ADI recommended settings for digitizer */
/* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG ( ADV76XX_PAGE_AFE , 0x12 ) , 0xfb } , /* ADC noise shaping filter controls */
{ ADV76XX_REG ( ADV76XX_PAGE_AFE , 0x0c ) , 0x0d } , /* CP core gain controls */
2013-11-25 22:45:07 +04:00
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG_SEQ_TERM , 0 } ,
2013-11-25 22:45:07 +04:00
} ;
2015-02-03 20:13:18 +03:00
static const struct adv76xx_reg_seq adv7611_recommended_settings_hdmi [ ] = {
2014-06-17 15:52:24 +04:00
/* ADV7611 Register Settings Recommendations Rev 1.5, May 2014 */
2015-02-03 20:13:18 +03:00
{ ADV76XX_REG ( ADV76XX_PAGE_CP , 0x6c ) , 0x00 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x9b ) , 0x03 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x6f ) , 0x08 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x85 ) , 0x1f } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x87 ) , 0x70 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x57 ) , 0xda } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x58 ) , 0x01 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x03 ) , 0x98 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x4c ) , 0x44 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8d ) , 0x04 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x8e ) , 0x1e } ,
{ ADV76XX_REG_SEQ_TERM , 0 } ,
2013-11-25 22:45:07 +04:00
} ;
2015-06-03 16:59:51 +03:00
static const struct adv76xx_reg_seq adv7612_recommended_settings_hdmi [ ] = {
{ ADV76XX_REG ( ADV76XX_PAGE_CP , 0x6c ) , 0x00 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x9b ) , 0x03 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x6f ) , 0x08 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x85 ) , 0x1f } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x87 ) , 0x70 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x57 ) , 0xda } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x58 ) , 0x01 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x03 ) , 0x98 } ,
{ ADV76XX_REG ( ADV76XX_PAGE_HDMI , 0x4c ) , 0x44 } ,
{ ADV76XX_REG_SEQ_TERM , 0 } ,
} ;
2015-02-03 20:13:18 +03:00
static const struct adv76xx_chip_info adv76xx_chip_info [ ] = {
2013-11-25 22:45:07 +04:00
[ ADV7604 ] = {
. type = ADV7604 ,
. has_afe = true ,
2014-01-29 17:08:58 +04:00
. max_port = ADV7604_PAD_VGA_COMP ,
2013-11-25 22:45:07 +04:00
. num_dv_ports = 4 ,
. edid_enable_reg = 0x77 ,
. edid_status_reg = 0x7d ,
. lcf_reg = 0xb3 ,
. tdms_lock_mask = 0xe0 ,
. cable_det_mask = 0x1e ,
. fmt_change_digital_mask = 0xc1 ,
2015-02-04 17:16:00 +03:00
. cp_csc = 0xfc ,
2018-10-12 14:30:02 +03:00
. cec_irq_status = 0x4d ,
. cec_rx_enable = 0x26 ,
. cec_rx_enable_mask = 0x01 ,
. cec_irq_swap = true ,
2014-01-27 01:42:37 +04:00
. formats = adv7604_formats ,
. nformats = ARRAY_SIZE ( adv7604_formats ) ,
2013-11-25 22:45:07 +04:00
. set_termination = adv7604_set_termination ,
. setup_irqs = adv7604_setup_irqs ,
. read_hdmi_pixelclock = adv7604_read_hdmi_pixelclock ,
. read_cable_det = adv7604_read_cable_det ,
. recommended_settings = {
[ 0 ] = adv7604_recommended_settings_afe ,
[ 1 ] = adv7604_recommended_settings_hdmi ,
} ,
. num_recommended_settings = {
[ 0 ] = ARRAY_SIZE ( adv7604_recommended_settings_afe ) ,
[ 1 ] = ARRAY_SIZE ( adv7604_recommended_settings_hdmi ) ,
} ,
2015-02-03 20:13:18 +03:00
. page_mask = BIT ( ADV76XX_PAGE_IO ) | BIT ( ADV7604_PAGE_AVLINK ) |
BIT ( ADV76XX_PAGE_CEC ) | BIT ( ADV76XX_PAGE_INFOFRAME ) |
2013-11-25 22:45:07 +04:00
BIT ( ADV7604_PAGE_ESDP ) | BIT ( ADV7604_PAGE_DPP ) |
2015-02-03 20:13:18 +03:00
BIT ( ADV76XX_PAGE_AFE ) | BIT ( ADV76XX_PAGE_REP ) |
BIT ( ADV76XX_PAGE_EDID ) | BIT ( ADV76XX_PAGE_HDMI ) |
BIT ( ADV76XX_PAGE_TEST ) | BIT ( ADV76XX_PAGE_CP ) |
2013-11-25 22:45:07 +04:00
BIT ( ADV7604_PAGE_VDP ) ,
2015-04-09 11:25:46 +03:00
. linewidth_mask = 0xfff ,
. field0_height_mask = 0xfff ,
. field1_height_mask = 0xfff ,
. hfrontporch_mask = 0x3ff ,
. hsync_mask = 0x3ff ,
. hbackporch_mask = 0x3ff ,
. field0_vfrontporch_mask = 0x1fff ,
. field0_vsync_mask = 0x1fff ,
. field0_vbackporch_mask = 0x1fff ,
. field1_vfrontporch_mask = 0x1fff ,
. field1_vsync_mask = 0x1fff ,
. field1_vbackporch_mask = 0x1fff ,
2013-11-25 22:45:07 +04:00
} ,
[ ADV7611 ] = {
. type = ADV7611 ,
. has_afe = false ,
2015-02-03 20:13:18 +03:00
. max_port = ADV76XX_PAD_HDMI_PORT_A ,
2013-11-25 22:45:07 +04:00
. num_dv_ports = 1 ,
. edid_enable_reg = 0x74 ,
. edid_status_reg = 0x76 ,
. lcf_reg = 0xa3 ,
. tdms_lock_mask = 0x43 ,
. cable_det_mask = 0x01 ,
. fmt_change_digital_mask = 0x03 ,
2015-02-04 17:16:00 +03:00
. cp_csc = 0xf4 ,
2018-10-12 14:30:02 +03:00
. cec_irq_status = 0x93 ,
. cec_rx_enable = 0x2c ,
. cec_rx_enable_mask = 0x02 ,
2014-01-27 01:42:37 +04:00
. formats = adv7611_formats ,
. nformats = ARRAY_SIZE ( adv7611_formats ) ,
2013-11-25 22:45:07 +04:00
. set_termination = adv7611_set_termination ,
. setup_irqs = adv7611_setup_irqs ,
. read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock ,
. read_cable_det = adv7611_read_cable_det ,
. recommended_settings = {
[ 1 ] = adv7611_recommended_settings_hdmi ,
} ,
. num_recommended_settings = {
[ 1 ] = ARRAY_SIZE ( adv7611_recommended_settings_hdmi ) ,
} ,
2015-02-03 20:13:18 +03:00
. page_mask = BIT ( ADV76XX_PAGE_IO ) | BIT ( ADV76XX_PAGE_CEC ) |
BIT ( ADV76XX_PAGE_INFOFRAME ) | BIT ( ADV76XX_PAGE_AFE ) |
BIT ( ADV76XX_PAGE_REP ) | BIT ( ADV76XX_PAGE_EDID ) |
BIT ( ADV76XX_PAGE_HDMI ) | BIT ( ADV76XX_PAGE_CP ) ,
2015-04-09 11:25:46 +03:00
. linewidth_mask = 0x1fff ,
. field0_height_mask = 0x1fff ,
. field1_height_mask = 0x1fff ,
. hfrontporch_mask = 0x1fff ,
. hsync_mask = 0x1fff ,
. hbackporch_mask = 0x1fff ,
. field0_vfrontporch_mask = 0x3fff ,
. field0_vsync_mask = 0x3fff ,
. field0_vbackporch_mask = 0x3fff ,
. field1_vfrontporch_mask = 0x3fff ,
. field1_vsync_mask = 0x3fff ,
. field1_vbackporch_mask = 0x3fff ,
2013-11-25 22:45:07 +04:00
} ,
2015-06-03 16:59:51 +03:00
[ ADV7612 ] = {
. type = ADV7612 ,
. has_afe = false ,
2015-07-23 15:21:34 +03:00
. max_port = ADV76XX_PAD_HDMI_PORT_A , /* B not supported */
. num_dv_ports = 1 , /* normally 2 */
2015-06-03 16:59:51 +03:00
. edid_enable_reg = 0x74 ,
. edid_status_reg = 0x76 ,
. lcf_reg = 0xa3 ,
. tdms_lock_mask = 0x43 ,
. cable_det_mask = 0x01 ,
. fmt_change_digital_mask = 0x03 ,
2015-07-23 15:21:34 +03:00
. cp_csc = 0xf4 ,
2018-10-12 14:30:02 +03:00
. cec_irq_status = 0x93 ,
. cec_rx_enable = 0x2c ,
. cec_rx_enable_mask = 0x02 ,
2015-06-03 16:59:51 +03:00
. formats = adv7612_formats ,
. nformats = ARRAY_SIZE ( adv7612_formats ) ,
. set_termination = adv7611_set_termination ,
. setup_irqs = adv7612_setup_irqs ,
. read_hdmi_pixelclock = adv7611_read_hdmi_pixelclock ,
2015-07-23 15:21:34 +03:00
. read_cable_det = adv7612_read_cable_det ,
2015-06-03 16:59:51 +03:00
. recommended_settings = {
[ 1 ] = adv7612_recommended_settings_hdmi ,
} ,
. num_recommended_settings = {
[ 1 ] = ARRAY_SIZE ( adv7612_recommended_settings_hdmi ) ,
} ,
. page_mask = BIT ( ADV76XX_PAGE_IO ) | BIT ( ADV76XX_PAGE_CEC ) |
BIT ( ADV76XX_PAGE_INFOFRAME ) | BIT ( ADV76XX_PAGE_AFE ) |
BIT ( ADV76XX_PAGE_REP ) | BIT ( ADV76XX_PAGE_EDID ) |
BIT ( ADV76XX_PAGE_HDMI ) | BIT ( ADV76XX_PAGE_CP ) ,
. linewidth_mask = 0x1fff ,
. field0_height_mask = 0x1fff ,
. field1_height_mask = 0x1fff ,
. hfrontporch_mask = 0x1fff ,
. hsync_mask = 0x1fff ,
. hbackporch_mask = 0x1fff ,
. field0_vfrontporch_mask = 0x3fff ,
. field0_vsync_mask = 0x3fff ,
. field0_vbackporch_mask = 0x3fff ,
. field1_vfrontporch_mask = 0x3fff ,
. field1_vsync_mask = 0x3fff ,
. field1_vbackporch_mask = 0x3fff ,
} ,
2013-11-25 22:45:07 +04:00
} ;
2015-03-16 22:54:33 +03:00
static const struct i2c_device_id adv76xx_i2c_id [ ] = {
2015-02-03 20:13:18 +03:00
{ " adv7604 " , ( kernel_ulong_t ) & adv76xx_chip_info [ ADV7604 ] } ,
{ " adv7611 " , ( kernel_ulong_t ) & adv76xx_chip_info [ ADV7611 ] } ,
2015-06-03 16:59:51 +03:00
{ " adv7612 " , ( kernel_ulong_t ) & adv76xx_chip_info [ ADV7612 ] } ,
2013-11-25 23:19:08 +04:00
{ }
} ;
2015-02-03 20:13:18 +03:00
MODULE_DEVICE_TABLE ( i2c , adv76xx_i2c_id ) ;
2013-11-25 23:19:08 +04:00
2015-03-16 22:54:33 +03:00
static const struct of_device_id adv76xx_of_id [ ] __maybe_unused = {
2015-02-03 20:13:18 +03:00
{ . compatible = " adi,adv7611 " , . data = & adv76xx_chip_info [ ADV7611 ] } ,
2015-06-03 16:59:51 +03:00
{ . compatible = " adi,adv7612 " , . data = & adv76xx_chip_info [ ADV7612 ] } ,
2013-11-25 23:19:08 +04:00
{ }
} ;
2015-02-03 20:13:18 +03:00
MODULE_DEVICE_TABLE ( of , adv76xx_of_id ) ;
2013-11-25 23:19:08 +04:00
2015-02-03 20:13:18 +03:00
static int adv76xx_parse_dt ( struct adv76xx_state * state )
2013-11-25 23:19:08 +04:00
{
2018-07-31 12:15:50 +03:00
struct v4l2_fwnode_endpoint bus_cfg = { . bus_type = 0 } ;
2014-02-05 03:23:16 +04:00
struct device_node * endpoint ;
struct device_node * np ;
unsigned int flags ;
2016-01-11 19:47:10 +03:00
int ret ;
2015-06-03 16:59:53 +03:00
u32 v ;
2014-02-05 03:23:16 +04:00
2015-02-03 20:13:18 +03:00
np = state - > i2c_clients [ ADV76XX_PAGE_IO ] - > dev . of_node ;
2014-02-05 03:23:16 +04:00
/* Parse the endpoint. */
endpoint = of_graph_get_next_endpoint ( np , NULL ) ;
if ( ! endpoint )
return - EINVAL ;
2016-08-27 02:17:25 +03:00
ret = v4l2_fwnode_endpoint_parse ( of_fwnode_handle ( endpoint ) , & bus_cfg ) ;
2016-09-22 16:19:00 +03:00
of_node_put ( endpoint ) ;
2018-06-01 12:21:32 +03:00
if ( ret )
return ret ;
2016-09-22 16:19:00 +03:00
if ( ! of_property_read_u32 ( np , " default-input " , & v ) )
2015-06-03 16:59:53 +03:00
state - > pdata . default_input = v ;
else
state - > pdata . default_input = - 1 ;
2014-02-05 03:23:16 +04:00
flags = bus_cfg . bus . parallel . flags ;
if ( flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH )
state - > pdata . inv_hs_pol = 1 ;
if ( flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH )
state - > pdata . inv_vs_pol = 1 ;
if ( flags & V4L2_MBUS_PCLK_SAMPLE_RISING )
state - > pdata . inv_llc_pol = 1 ;
2016-06-28 17:43:01 +03:00
if ( bus_cfg . bus_type = = V4L2_MBUS_BT656 )
2014-02-05 03:23:16 +04:00
state - > pdata . insert_av_codes = 1 ;
2013-11-25 23:19:08 +04:00
/* Disable the interrupt for now as no DT-based board uses it. */
2018-10-12 14:30:02 +03:00
state - > pdata . int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH ;
2013-11-25 23:19:08 +04:00
/* Hardcode the remaining platform data fields. */
state - > pdata . disable_pwrdnb = 0 ;
state - > pdata . disable_cable_det_rst = 0 ;
state - > pdata . blank_data = 1 ;
state - > pdata . op_format_mode_sel = ADV7604_OP_FORMAT_MODE0 ;
state - > pdata . bus_order = ADV7604_BUS_ORDER_RGB ;
2016-11-29 14:23:48 +03:00
state - > pdata . dr_str_data = ADV76XX_DR_STR_MEDIUM_HIGH ;
state - > pdata . dr_str_clk = ADV76XX_DR_STR_MEDIUM_HIGH ;
state - > pdata . dr_str_sync = ADV76XX_DR_STR_MEDIUM_HIGH ;
2013-11-25 23:19:08 +04:00
return 0 ;
}
2015-06-19 16:23:06 +03:00
static const struct regmap_config adv76xx_regmap_cnf [ ] = {
{
. name = " io " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " avlink " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " cec " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " infoframe " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " esdp " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " epp " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " afe " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " rep " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " edid " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " hdmi " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " test " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " cp " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
{
. name = " vdp " ,
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0xff ,
. cache_type = REGCACHE_NONE ,
} ,
} ;
static int configure_regmap ( struct adv76xx_state * state , int region )
{
int err ;
if ( ! state - > i2c_clients [ region ] )
return - ENODEV ;
state - > regmap [ region ] =
devm_regmap_init_i2c ( state - > i2c_clients [ region ] ,
& adv76xx_regmap_cnf [ region ] ) ;
if ( IS_ERR ( state - > regmap [ region ] ) ) {
err = PTR_ERR ( state - > regmap [ region ] ) ;
v4l_err ( state - > i2c_clients [ region ] ,
" Error initializing regmap %d with error %d \n " ,
region , err ) ;
return - EINVAL ;
}
return 0 ;
}
static int configure_regmaps ( struct adv76xx_state * state )
{
int i , err ;
for ( i = ADV7604_PAGE_AVLINK ; i < ADV76XX_PAGE_MAX ; i + + ) {
err = configure_regmap ( state , i ) ;
if ( err & & ( err ! = - ENODEV ) )
return err ;
}
return 0 ;
}
2016-06-22 14:30:42 +03:00
static void adv76xx_reset ( struct adv76xx_state * state )
{
if ( state - > reset_gpio ) {
/* ADV76XX can be reset by a low reset pulse of minimum 5 ms. */
gpiod_set_value_cansleep ( state - > reset_gpio , 0 ) ;
usleep_range ( 5000 , 10000 ) ;
gpiod_set_value_cansleep ( state - > reset_gpio , 1 ) ;
/* It is recommended to wait 5 ms after the low pulse before */
/* an I2C write is performed to the ADV76XX. */
usleep_range ( 5000 , 10000 ) ;
}
}
2015-02-03 20:13:18 +03:00
static int adv76xx_probe ( struct i2c_client * client ,
2012-07-18 12:45:16 +04:00
const struct i2c_device_id * id )
{
2013-12-17 17:05:13 +04:00
static const struct v4l2_dv_timings cea640x480 =
V4L2_DV_BT_CEA_640X480P59_94 ;
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state ;
2012-07-18 12:45:16 +04:00
struct v4l2_ctrl_handler * hdl ;
2016-01-27 16:31:41 +03:00
struct v4l2_ctrl * ctrl ;
2012-07-18 12:45:16 +04:00
struct v4l2_subdev * sd ;
2014-01-29 17:08:58 +04:00
unsigned int i ;
2015-06-19 16:23:06 +03:00
unsigned int val , val2 ;
2012-07-18 12:45:16 +04:00
int err ;
/* Check if the adapter supports the needed features */
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - EIO ;
2015-02-03 20:13:18 +03:00
v4l_dbg ( 1 , debug , client , " detecting adv76xx client on address 0x%x \n " ,
2012-07-18 12:45:16 +04:00
client - > addr < < 1 ) ;
2013-05-02 15:29:43 +04:00
state = devm_kzalloc ( & client - > dev , sizeof ( * state ) , GFP_KERNEL ) ;
2017-08-28 12:46:57 +03:00
if ( ! state )
2012-07-18 12:45:16 +04:00
return - ENOMEM ;
2015-02-03 20:13:18 +03:00
state - > i2c_clients [ ADV76XX_PAGE_IO ] = client ;
2013-11-25 22:45:07 +04:00
2013-08-14 14:58:45 +04:00
/* initialize variables */
state - > restart_stdi_once = true ;
2013-12-05 17:24:05 +04:00
state - > selected_input = ~ 0 ;
2013-08-14 14:58:45 +04:00
2013-11-25 23:19:08 +04:00
if ( IS_ENABLED ( CONFIG_OF ) & & client - > dev . of_node ) {
const struct of_device_id * oid ;
2015-02-03 20:13:18 +03:00
oid = of_match_node ( adv76xx_of_id , client - > dev . of_node ) ;
2013-11-25 23:19:08 +04:00
state - > info = oid - > data ;
2015-02-03 20:13:18 +03:00
err = adv76xx_parse_dt ( state ) ;
2013-11-25 23:19:08 +04:00
if ( err < 0 ) {
v4l_err ( client , " DT parsing error \n " ) ;
return err ;
}
} else if ( client - > dev . platform_data ) {
2015-02-03 20:13:18 +03:00
struct adv76xx_platform_data * pdata = client - > dev . platform_data ;
2013-11-25 23:19:08 +04:00
2015-02-03 20:13:18 +03:00
state - > info = ( const struct adv76xx_chip_info * ) id - > driver_data ;
2013-11-25 23:19:08 +04:00
state - > pdata = * pdata ;
} else {
2012-07-18 12:45:16 +04:00
v4l_err ( client , " No platform data! \n " ) ;
2013-05-02 15:29:43 +04:00
return - ENODEV ;
2012-07-18 12:45:16 +04:00
}
2014-01-31 01:37:08 +04:00
/* Request GPIOs. */
for ( i = 0 ; i < state - > info - > num_dv_ports ; + + i ) {
state - > hpd_gpio [ i ] =
2015-03-02 10:00:44 +03:00
devm_gpiod_get_index_optional ( & client - > dev , " hpd " , i ,
GPIOD_OUT_LOW ) ;
2014-01-31 01:37:08 +04:00
if ( IS_ERR ( state - > hpd_gpio [ i ] ) )
2015-03-02 10:00:44 +03:00
return PTR_ERR ( state - > hpd_gpio [ i ] ) ;
2014-01-31 01:37:08 +04:00
2015-03-02 10:00:44 +03:00
if ( state - > hpd_gpio [ i ] )
v4l_info ( client , " Handling HPD %u GPIO \n " , i ) ;
2014-01-31 01:37:08 +04:00
}
2016-06-22 14:30:42 +03:00
state - > reset_gpio = devm_gpiod_get_optional ( & client - > dev , " reset " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( state - > reset_gpio ) )
return PTR_ERR ( state - > reset_gpio ) ;
adv76xx_reset ( state ) ;
2014-01-31 01:37:08 +04:00
2013-12-17 17:05:13 +04:00
state - > timings = cea640x480 ;
2015-02-03 20:13:18 +03:00
state - > format = adv76xx_format_info ( state , MEDIA_BUS_FMT_YUYV8_2X8 ) ;
2012-07-18 12:45:16 +04:00
sd = & state - > sd ;
2015-02-03 20:13:18 +03:00
v4l2_i2c_subdev_init ( sd , client , & adv76xx_ops ) ;
2013-11-25 22:45:07 +04:00
snprintf ( sd - > name , sizeof ( sd - > name ) , " %s %d-%04x " ,
id - > name , i2c_adapter_id ( client - > adapter ) ,
client - > addr ) ;
2015-06-24 19:50:27 +03:00
sd - > flags | = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS ;
2015-09-07 14:12:57 +03:00
sd - > internal_ops = & adv76xx_int_ops ;
2012-07-18 12:45:16 +04:00
2015-06-19 16:23:06 +03:00
/* Configure IO Regmap region */
err = configure_regmap ( state , ADV76XX_PAGE_IO ) ;
if ( err ) {
v4l2_err ( sd , " Error configuring IO regmap region \n " ) ;
return - ENODEV ;
}
2013-11-25 22:45:07 +04:00
/*
* Verify that the chip is present . On ADV7604 the RD_INFO register only
* identifies the revision , while on ADV7611 it identifies the model as
* well . Use the HDMI slave address on ADV7604 and RD_INFO on ADV7611 .
*/
2015-06-03 16:59:51 +03:00
switch ( state - > info - > type ) {
case ADV7604 :
2015-06-19 16:23:06 +03:00
err = regmap_read ( state - > regmap [ ADV76XX_PAGE_IO ] , 0xfb , & val ) ;
if ( err ) {
v4l2_err ( sd , " Error %d reading IO Regmap \n " , err ) ;
return - ENODEV ;
}
2013-11-25 22:45:07 +04:00
if ( val ! = 0x68 ) {
2015-06-19 16:23:06 +03:00
v4l2_err ( sd , " not an adv7604 on address 0x%x \n " ,
2013-11-25 22:45:07 +04:00
client - > addr < < 1 ) ;
return - ENODEV ;
}
2015-06-03 16:59:51 +03:00
break ;
case ADV7611 :
case ADV7612 :
2015-06-19 16:23:06 +03:00
err = regmap_read ( state - > regmap [ ADV76XX_PAGE_IO ] ,
0xea ,
& val ) ;
if ( err ) {
v4l2_err ( sd , " Error %d reading IO Regmap \n " , err ) ;
return - ENODEV ;
}
val2 = val < < 8 ;
err = regmap_read ( state - > regmap [ ADV76XX_PAGE_IO ] ,
0xeb ,
& val ) ;
if ( err ) {
v4l2_err ( sd , " Error %d reading IO Regmap \n " , err ) ;
return - ENODEV ;
}
2015-07-23 15:21:33 +03:00
val | = val2 ;
2015-06-03 16:59:51 +03:00
if ( ( state - > info - > type = = ADV7611 & & val ! = 0x2051 ) | |
( state - > info - > type = = ADV7612 & & val ! = 0x2041 ) ) {
v4l2_err ( sd , " not an adv761x on address 0x%x \n " ,
2013-11-25 22:45:07 +04:00
client - > addr < < 1 ) ;
return - ENODEV ;
}
2015-06-03 16:59:51 +03:00
break ;
2012-07-18 12:45:16 +04:00
}
/* control handlers */
hdl = & state - > hdl ;
2015-02-03 20:13:18 +03:00
v4l2_ctrl_handler_init ( hdl , adv76xx_has_afe ( state ) ? 9 : 8 ) ;
2012-07-18 12:45:16 +04:00
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_std ( hdl , & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
V4L2_CID_BRIGHTNESS , - 128 , 127 , 1 , 0 ) ;
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_std ( hdl , & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
V4L2_CID_CONTRAST , 0 , 255 , 1 , 128 ) ;
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_std ( hdl , & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
V4L2_CID_SATURATION , 0 , 255 , 1 , 128 ) ;
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_std ( hdl , & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
V4L2_CID_HUE , 0 , 128 , 1 , 0 ) ;
2016-01-27 16:31:41 +03:00
ctrl = v4l2_ctrl_new_std_menu ( hdl , & adv76xx_ctrl_ops ,
V4L2_CID_DV_RX_IT_CONTENT_TYPE , V4L2_DV_IT_CONTENT_TYPE_NO_ITC ,
0 , V4L2_DV_IT_CONTENT_TYPE_NO_ITC ) ;
if ( ctrl )
ctrl - > flags | = V4L2_CTRL_FLAG_VOLATILE ;
2012-07-18 12:45:16 +04:00
state - > detect_tx_5v_ctrl = v4l2_ctrl_new_std ( hdl , NULL ,
2013-11-25 22:45:07 +04:00
V4L2_CID_DV_RX_POWER_PRESENT , 0 ,
( 1 < < state - > info - > num_dv_ports ) - 1 , 0 , 0 ) ;
2012-07-18 12:45:16 +04:00
state - > rgb_quantization_range_ctrl =
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_std_menu ( hdl , & adv76xx_ctrl_ops ,
2012-07-18 12:45:16 +04:00
V4L2_CID_DV_RX_RGB_RANGE , V4L2_DV_RGB_RANGE_FULL ,
0 , V4L2_DV_RGB_RANGE_AUTO ) ;
/* custom controls */
2015-02-03 20:13:18 +03:00
if ( adv76xx_has_afe ( state ) )
2013-11-25 22:45:07 +04:00
state - > analog_sampling_phase_ctrl =
v4l2_ctrl_new_custom ( hdl , & adv7604_ctrl_analog_sampling_phase , NULL ) ;
2012-07-18 12:45:16 +04:00
state - > free_run_color_manual_ctrl =
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_custom ( hdl , & adv76xx_ctrl_free_run_color_manual , NULL ) ;
2012-07-18 12:45:16 +04:00
state - > free_run_color_ctrl =
2015-02-03 20:13:18 +03:00
v4l2_ctrl_new_custom ( hdl , & adv76xx_ctrl_free_run_color , NULL ) ;
2012-07-18 12:45:16 +04:00
sd - > ctrl_handler = hdl ;
if ( hdl - > error ) {
err = hdl - > error ;
goto err_hdl ;
}
2015-02-03 20:13:18 +03:00
if ( adv76xx_s_detect_tx_5v_ctrl ( sd ) ) {
2012-07-18 12:45:16 +04:00
err = - ENODEV ;
goto err_hdl ;
}
2015-02-03 20:13:18 +03:00
for ( i = 1 ; i < ADV76XX_PAGE_MAX ; + + i ) {
2019-08-09 18:40:47 +03:00
struct i2c_client * dummy_client ;
2014-01-30 23:32:21 +04:00
if ( ! ( BIT ( i ) & state - > info - > page_mask ) )
continue ;
2012-07-18 12:45:16 +04:00
2019-08-09 18:40:47 +03:00
dummy_client = adv76xx_dummy_client ( sd , i ) ;
if ( IS_ERR ( dummy_client ) ) {
err = PTR_ERR ( dummy_client ) ;
2014-01-30 23:32:21 +04:00
v4l2_err ( sd , " failed to create i2c client %u \n " , i ) ;
2013-11-25 22:45:07 +04:00
goto err_i2c ;
}
2019-08-09 18:40:47 +03:00
state - > i2c_clients [ i ] = dummy_client ;
2013-11-25 22:45:07 +04:00
}
2014-01-30 23:32:21 +04:00
2012-07-18 12:45:16 +04:00
INIT_DELAYED_WORK ( & state - > delayed_work_enable_hotplug ,
2015-02-03 20:13:18 +03:00
adv76xx_delayed_work_enable_hotplug ) ;
2012-07-18 12:45:16 +04:00
2014-01-29 17:08:58 +04:00
state - > source_pad = state - > info - > num_dv_ports
+ ( state - > info - > has_afe ? 2 : 0 ) ;
for ( i = 0 ; i < state - > source_pad ; + + i )
state - > pads [ i ] . flags = MEDIA_PAD_FL_SINK ;
state - > pads [ state - > source_pad ] . flags = MEDIA_PAD_FL_SOURCE ;
2018-06-28 15:56:02 +03:00
sd - > entity . function = MEDIA_ENT_F_DV_DECODER ;
2014-01-29 17:08:58 +04:00
2015-12-11 12:44:40 +03:00
err = media_entity_pads_init ( & sd - > entity , state - > source_pad + 1 ,
2015-08-06 15:25:57 +03:00
state - > pads ) ;
2012-07-18 12:45:16 +04:00
if ( err )
goto err_work_queues ;
2015-06-19 16:23:06 +03:00
/* Configure regmaps */
err = configure_regmaps ( state ) ;
if ( err )
goto err_entity ;
2015-02-03 20:13:18 +03:00
err = adv76xx_core_init ( sd ) ;
2012-07-18 12:45:16 +04:00
if ( err )
goto err_entity ;
2015-09-07 14:12:57 +03:00
2018-10-12 14:30:02 +03:00
if ( client - > irq ) {
err = devm_request_threaded_irq ( & client - > dev ,
client - > irq ,
NULL , adv76xx_irq_handler ,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT ,
client - > name , state ) ;
if ( err )
goto err_entity ;
}
2015-09-07 14:12:57 +03:00
# if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC)
state - > cec_adap = cec_allocate_adapter ( & adv76xx_cec_adap_ops ,
state , dev_name ( & client - > dev ) ,
2017-08-04 13:41:52 +03:00
CEC_CAP_DEFAULTS , ADV76XX_MAX_ADDRS ) ;
2015-09-07 14:12:57 +03:00
err = PTR_ERR_OR_ZERO ( state - > cec_adap ) ;
if ( err )
goto err_entity ;
# endif
2012-07-18 12:45:16 +04:00
v4l2_info ( sd , " %s found @ 0x%x (%s) \n " , client - > name ,
client - > addr < < 1 , client - > adapter - > name ) ;
2013-11-25 23:18:02 +04:00
err = v4l2_async_register_subdev ( sd ) ;
if ( err )
goto err_entity ;
2012-07-18 12:45:16 +04:00
return 0 ;
err_entity :
media_entity_cleanup ( & sd - > entity ) ;
err_work_queues :
cancel_delayed_work ( & state - > delayed_work_enable_hotplug ) ;
err_i2c :
2015-02-03 20:13:18 +03:00
adv76xx_unregister_clients ( state ) ;
2012-07-18 12:45:16 +04:00
err_hdl :
v4l2_ctrl_handler_free ( hdl ) ;
return err ;
}
/* ----------------------------------------------------------------------- */
2015-02-03 20:13:18 +03:00
static int adv76xx_remove ( struct i2c_client * client )
2012-07-18 12:45:16 +04:00
{
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
2015-02-03 20:13:18 +03:00
struct adv76xx_state * state = to_state ( sd ) ;
2012-07-18 12:45:16 +04:00
2015-09-07 14:12:57 +03:00
/* disable interrupts */
io_write ( sd , 0x40 , 0 ) ;
io_write ( sd , 0x41 , 0 ) ;
io_write ( sd , 0x46 , 0 ) ;
io_write ( sd , 0x6e , 0 ) ;
io_write ( sd , 0x73 , 0 ) ;
2012-07-18 12:45:16 +04:00
cancel_delayed_work ( & state - > delayed_work_enable_hotplug ) ;
2013-11-25 23:18:02 +04:00
v4l2_async_unregister_subdev ( sd ) ;
2012-07-18 12:45:16 +04:00
media_entity_cleanup ( & sd - > entity ) ;
2015-02-03 20:13:18 +03:00
adv76xx_unregister_clients ( to_state ( sd ) ) ;
2012-07-18 12:45:16 +04:00
v4l2_ctrl_handler_free ( sd - > ctrl_handler ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
2015-02-03 20:13:18 +03:00
static struct i2c_driver adv76xx_driver = {
2012-07-18 12:45:16 +04:00
. driver = {
. name = " adv7604 " ,
2015-02-03 20:13:18 +03:00
. of_match_table = of_match_ptr ( adv76xx_of_id ) ,
2012-07-18 12:45:16 +04:00
} ,
2015-02-03 20:13:18 +03:00
. probe = adv76xx_probe ,
. remove = adv76xx_remove ,
. id_table = adv76xx_i2c_id ,
2012-07-18 12:45:16 +04:00
} ;
2015-02-03 20:13:18 +03:00
module_i2c_driver ( adv76xx_driver ) ;