2013-06-18 09:23:57 +08:00
/*
* i . MX drm driver - LVDS display bridge
*
* Copyright ( C ) 2012 Sascha Hauer , Pengutronix
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/clk.h>
2013-11-03 11:23:34 +00:00
# include <linux/component.h>
2013-06-18 09:23:57 +08:00
# include <drm/drmP.h>
2016-07-06 14:49:24 +02:00
# include <drm/drm_atomic.h>
2016-07-08 17:40:56 +08:00
# include <drm/drm_atomic_helper.h>
2013-06-18 09:23:57 +08:00
# include <drm/drm_fb_helper.h>
# include <drm/drm_crtc_helper.h>
2015-02-24 11:41:28 +01:00
# include <drm/drm_of.h>
2014-03-06 14:54:39 +01:00
# include <drm/drm_panel.h>
2013-06-18 09:23:57 +08:00
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
# include <linux/of_device.h>
2014-03-06 14:54:39 +01:00
# include <linux/of_graph.h>
2016-05-24 08:31:49 +02:00
# include <video/of_display_timing.h>
2013-06-18 09:23:57 +08:00
# include <video/of_videomode.h>
# include <linux/regmap.h>
# include <linux/videodev2.h>
# include "imx-drm.h"
# define DRIVER_NAME "imx-ldb"
# define LDB_CH0_MODE_EN_TO_DI0 (1 << 0)
# define LDB_CH0_MODE_EN_TO_DI1 (3 << 0)
# define LDB_CH0_MODE_EN_MASK (3 << 0)
# define LDB_CH1_MODE_EN_TO_DI0 (1 << 2)
# define LDB_CH1_MODE_EN_TO_DI1 (3 << 2)
# define LDB_CH1_MODE_EN_MASK (3 << 2)
# define LDB_SPLIT_MODE_EN (1 << 4)
# define LDB_DATA_WIDTH_CH0_24 (1 << 5)
# define LDB_BIT_MAP_CH0_JEIDA (1 << 6)
# define LDB_DATA_WIDTH_CH1_24 (1 << 7)
# define LDB_BIT_MAP_CH1_JEIDA (1 << 8)
# define LDB_DI0_VS_POL_ACT_LOW (1 << 9)
# define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
# define LDB_BGREF_RMODE_INT (1 << 15)
struct imx_ldb ;
struct imx_ldb_channel {
struct imx_ldb * ldb ;
struct drm_connector connector ;
2016-07-06 14:49:24 +02:00
struct drm_encoder encoder ;
2014-03-06 14:54:39 +01:00
struct drm_panel * panel ;
2013-11-03 13:30:48 +00:00
struct device_node * child ;
2016-04-27 16:23:33 -04:00
struct i2c_adapter * ddc ;
2013-06-18 09:23:57 +08:00
int chno ;
void * edid ;
int edid_len ;
struct drm_display_mode mode ;
int mode_valid ;
2016-07-06 14:49:24 +02:00
u32 bus_format ;
2013-06-18 09:23:57 +08:00
} ;
2016-07-06 15:47:11 +02:00
static inline struct imx_ldb_channel * con_to_imx_ldb_ch ( struct drm_connector * c )
{
return container_of ( c , struct imx_ldb_channel , connector ) ;
}
2016-07-06 14:49:24 +02:00
static inline struct imx_ldb_channel * enc_to_imx_ldb_ch ( struct drm_encoder * e )
{
return container_of ( e , struct imx_ldb_channel , encoder ) ;
}
2013-06-18 09:23:57 +08:00
struct bus_mux {
int reg ;
int shift ;
int mask ;
} ;
struct imx_ldb {
struct regmap * regmap ;
struct device * dev ;
struct imx_ldb_channel channel [ 2 ] ;
struct clk * clk [ 2 ] ; /* our own clock */
struct clk * clk_sel [ 4 ] ; /* parent of display clock */
2014-11-26 13:59:11 +01:00
struct clk * clk_parent [ 4 ] ; /* original parent of clk_sel */
2013-06-18 09:23:57 +08:00
struct clk * clk_pll [ 2 ] ; /* upstream clock we can adjust */
u32 ldb_ctrl ;
const struct bus_mux * lvds_mux ;
} ;
static enum drm_connector_status imx_ldb_connector_detect (
struct drm_connector * connector , bool force )
{
return connector_status_connected ;
}
2016-07-06 14:49:24 +02:00
static void imx_ldb_ch_set_bus_format ( struct imx_ldb_channel * imx_ldb_ch ,
u32 bus_format )
2016-07-08 17:40:58 +08:00
{
struct imx_ldb * ldb = imx_ldb_ch - > ldb ;
int dual = ldb - > ldb_ctrl & LDB_SPLIT_MODE_EN ;
switch ( bus_format ) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG :
break ;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG :
if ( imx_ldb_ch - > chno = = 0 | | dual )
ldb - > ldb_ctrl | = LDB_DATA_WIDTH_CH0_24 ;
if ( imx_ldb_ch - > chno = = 1 | | dual )
ldb - > ldb_ctrl | = LDB_DATA_WIDTH_CH1_24 ;
break ;
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA :
if ( imx_ldb_ch - > chno = = 0 | | dual )
ldb - > ldb_ctrl | = LDB_DATA_WIDTH_CH0_24 |
LDB_BIT_MAP_CH0_JEIDA ;
if ( imx_ldb_ch - > chno = = 1 | | dual )
ldb - > ldb_ctrl | = LDB_DATA_WIDTH_CH1_24 |
LDB_BIT_MAP_CH1_JEIDA ;
break ;
}
}
2013-06-18 09:23:57 +08:00
static int imx_ldb_connector_get_modes ( struct drm_connector * connector )
{
struct imx_ldb_channel * imx_ldb_ch = con_to_imx_ldb_ch ( connector ) ;
int num_modes = 0 ;
2014-03-06 14:54:39 +01:00
if ( imx_ldb_ch - > panel & & imx_ldb_ch - > panel - > funcs & &
imx_ldb_ch - > panel - > funcs - > get_modes ) {
num_modes = imx_ldb_ch - > panel - > funcs - > get_modes ( imx_ldb_ch - > panel ) ;
if ( num_modes > 0 )
return num_modes ;
}
2016-04-27 16:23:33 -04:00
if ( ! imx_ldb_ch - > edid & & imx_ldb_ch - > ddc )
imx_ldb_ch - > edid = drm_get_edid ( connector , imx_ldb_ch - > ddc ) ;
2013-06-18 09:23:57 +08:00
if ( imx_ldb_ch - > edid ) {
drm_mode_connector_update_edid_property ( connector ,
imx_ldb_ch - > edid ) ;
num_modes = drm_add_edid_modes ( connector , imx_ldb_ch - > edid ) ;
}
if ( imx_ldb_ch - > mode_valid ) {
struct drm_display_mode * mode ;
mode = drm_mode_create ( connector - > dev ) ;
2014-02-26 20:53:40 -03:00
if ( ! mode )
return - EINVAL ;
2013-06-18 09:23:57 +08:00
drm_mode_copy ( mode , & imx_ldb_ch - > mode ) ;
mode - > type | = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED ;
drm_mode_probed_add ( connector , mode ) ;
num_modes + + ;
}
return num_modes ;
}
static struct drm_encoder * imx_ldb_connector_best_encoder (
struct drm_connector * connector )
{
struct imx_ldb_channel * imx_ldb_ch = con_to_imx_ldb_ch ( connector ) ;
2016-07-06 14:49:24 +02:00
return & imx_ldb_ch - > encoder ;
2013-06-18 09:23:57 +08:00
}
static void imx_ldb_set_clock ( struct imx_ldb * ldb , int mux , int chno ,
unsigned long serial_clk , unsigned long di_clk )
{
int ret ;
dev_dbg ( ldb - > dev , " %s: now: %ld want: %ld \n " , __func__ ,
clk_get_rate ( ldb - > clk_pll [ chno ] ) , serial_clk ) ;
clk_set_rate ( ldb - > clk_pll [ chno ] , serial_clk ) ;
dev_dbg ( ldb - > dev , " %s after: %ld \n " , __func__ ,
clk_get_rate ( ldb - > clk_pll [ chno ] ) ) ;
dev_dbg ( ldb - > dev , " %s: now: %ld want: %ld \n " , __func__ ,
clk_get_rate ( ldb - > clk [ chno ] ) ,
( long int ) di_clk ) ;
clk_set_rate ( ldb - > clk [ chno ] , di_clk ) ;
dev_dbg ( ldb - > dev , " %s after: %ld \n " , __func__ ,
clk_get_rate ( ldb - > clk [ chno ] ) ) ;
/* set display clock mux to LDB input clock */
ret = clk_set_parent ( ldb - > clk_sel [ mux ] , ldb - > clk [ chno ] ) ;
2013-11-03 11:18:57 +01:00
if ( ret )
2014-03-17 23:55:53 +02:00
dev_err ( ldb - > dev ,
" unable to set di%d parent clock to ldb_di%d \n " , mux ,
chno ) ;
2013-06-18 09:23:57 +08:00
}
2016-07-08 17:41:01 +08:00
static void imx_ldb_encoder_enable ( struct drm_encoder * encoder )
2013-06-18 09:23:57 +08:00
{
2016-07-06 14:49:24 +02:00
struct imx_ldb_channel * imx_ldb_ch = enc_to_imx_ldb_ch ( encoder ) ;
2013-06-18 09:23:57 +08:00
struct imx_ldb * ldb = imx_ldb_ch - > ldb ;
int dual = ldb - > ldb_ctrl & LDB_SPLIT_MODE_EN ;
2015-02-24 11:41:28 +01:00
int mux = drm_of_encoder_active_port_id ( imx_ldb_ch - > child , encoder ) ;
2013-06-18 09:23:57 +08:00
2014-03-06 14:54:39 +01:00
drm_panel_prepare ( imx_ldb_ch - > panel ) ;
2013-06-18 09:23:57 +08:00
if ( dual ) {
2016-07-08 17:41:01 +08:00
clk_set_parent ( ldb - > clk_sel [ mux ] , ldb - > clk [ 0 ] ) ;
clk_set_parent ( ldb - > clk_sel [ mux ] , ldb - > clk [ 1 ] ) ;
2013-06-18 09:23:57 +08:00
clk_prepare_enable ( ldb - > clk [ 0 ] ) ;
clk_prepare_enable ( ldb - > clk [ 1 ] ) ;
2016-07-08 17:41:01 +08:00
} else {
clk_set_parent ( ldb - > clk_sel [ mux ] , ldb - > clk [ imx_ldb_ch - > chno ] ) ;
2013-06-18 09:23:57 +08:00
}
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] | | dual ) {
ldb - > ldb_ctrl & = ~ LDB_CH0_MODE_EN_MASK ;
if ( mux = = 0 | | ldb - > lvds_mux )
ldb - > ldb_ctrl | = LDB_CH0_MODE_EN_TO_DI0 ;
else if ( mux = = 1 )
ldb - > ldb_ctrl | = LDB_CH0_MODE_EN_TO_DI1 ;
}
if ( imx_ldb_ch = = & ldb - > channel [ 1 ] | | dual ) {
ldb - > ldb_ctrl & = ~ LDB_CH1_MODE_EN_MASK ;
if ( mux = = 1 | | ldb - > lvds_mux )
ldb - > ldb_ctrl | = LDB_CH1_MODE_EN_TO_DI1 ;
else if ( mux = = 0 )
ldb - > ldb_ctrl | = LDB_CH1_MODE_EN_TO_DI0 ;
}
if ( ldb - > lvds_mux ) {
const struct bus_mux * lvds_mux = NULL ;
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] )
lvds_mux = & ldb - > lvds_mux [ 0 ] ;
else if ( imx_ldb_ch = = & ldb - > channel [ 1 ] )
lvds_mux = & ldb - > lvds_mux [ 1 ] ;
regmap_update_bits ( ldb - > regmap , lvds_mux - > reg , lvds_mux - > mask ,
mux < < lvds_mux - > shift ) ;
}
regmap_write ( ldb - > regmap , IOMUXC_GPR2 , ldb - > ldb_ctrl ) ;
2014-03-06 14:54:39 +01:00
drm_panel_enable ( imx_ldb_ch - > panel ) ;
2013-06-18 09:23:57 +08:00
}
static void imx_ldb_encoder_mode_set ( struct drm_encoder * encoder ,
2014-12-18 18:00:24 -08:00
struct drm_display_mode * orig_mode ,
struct drm_display_mode * mode )
2013-06-18 09:23:57 +08:00
{
2016-07-06 14:49:24 +02:00
struct imx_ldb_channel * imx_ldb_ch = enc_to_imx_ldb_ch ( encoder ) ;
2013-06-18 09:23:57 +08:00
struct imx_ldb * ldb = imx_ldb_ch - > ldb ;
int dual = ldb - > ldb_ctrl & LDB_SPLIT_MODE_EN ;
2015-01-23 17:10:01 +01:00
unsigned long serial_clk ;
unsigned long di_clk = mode - > clock * 1000 ;
2015-02-24 11:41:28 +01:00
int mux = drm_of_encoder_active_port_id ( imx_ldb_ch - > child , encoder ) ;
2016-07-06 14:49:24 +02:00
u32 bus_format = imx_ldb_ch - > bus_format ;
2013-06-18 09:23:57 +08:00
if ( mode - > clock > 170000 ) {
dev_warn ( ldb - > dev ,
" %s: mode exceeds 170 MHz pixel clock \n " , __func__ ) ;
}
if ( mode - > clock > 85000 & & ! dual ) {
dev_warn ( ldb - > dev ,
" %s: mode exceeds 85 MHz pixel clock \n " , __func__ ) ;
}
2015-01-23 17:10:01 +01:00
if ( dual ) {
serial_clk = 3500UL * mode - > clock ;
imx_ldb_set_clock ( ldb , mux , 0 , serial_clk , di_clk ) ;
imx_ldb_set_clock ( ldb , mux , 1 , serial_clk , di_clk ) ;
} else {
serial_clk = 7000UL * mode - > clock ;
imx_ldb_set_clock ( ldb , mux , imx_ldb_ch - > chno , serial_clk ,
di_clk ) ;
}
2013-06-18 09:23:57 +08:00
/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
2016-07-06 14:49:24 +02:00
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] | | dual ) {
2013-06-18 09:23:57 +08:00
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
ldb - > ldb_ctrl | = LDB_DI0_VS_POL_ACT_LOW ;
else if ( mode - > flags & DRM_MODE_FLAG_PVSYNC )
ldb - > ldb_ctrl & = ~ LDB_DI0_VS_POL_ACT_LOW ;
}
2016-07-06 14:49:24 +02:00
if ( imx_ldb_ch = = & ldb - > channel [ 1 ] | | dual ) {
2013-06-18 09:23:57 +08:00
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
ldb - > ldb_ctrl | = LDB_DI1_VS_POL_ACT_LOW ;
else if ( mode - > flags & DRM_MODE_FLAG_PVSYNC )
ldb - > ldb_ctrl & = ~ LDB_DI1_VS_POL_ACT_LOW ;
}
2016-07-06 14:49:24 +02:00
if ( ! bus_format ) {
struct drm_connector_state * conn_state ;
struct drm_connector * connector ;
int i ;
for_each_connector_in_state ( encoder - > crtc - > state - > state ,
connector , conn_state , i ) {
struct drm_display_info * di = & connector - > display_info ;
if ( conn_state - > crtc = = encoder - > crtc & &
di - > num_bus_formats ) {
bus_format = di - > bus_formats [ 0 ] ;
break ;
}
}
}
imx_ldb_ch_set_bus_format ( imx_ldb_ch , bus_format ) ;
2013-06-18 09:23:57 +08:00
}
static void imx_ldb_encoder_disable ( struct drm_encoder * encoder )
{
2016-07-06 14:49:24 +02:00
struct imx_ldb_channel * imx_ldb_ch = enc_to_imx_ldb_ch ( encoder ) ;
2013-06-18 09:23:57 +08:00
struct imx_ldb * ldb = imx_ldb_ch - > ldb ;
2014-11-26 13:59:11 +01:00
int mux , ret ;
2013-06-18 09:23:57 +08:00
/*
* imx_ldb_encoder_disable is called by
* drm_helper_disable_unused_functions without
* the encoder being enabled before .
*/
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] & &
( ldb - > ldb_ctrl & LDB_CH0_MODE_EN_MASK ) = = 0 )
return ;
else if ( imx_ldb_ch = = & ldb - > channel [ 1 ] & &
( ldb - > ldb_ctrl & LDB_CH1_MODE_EN_MASK ) = = 0 )
return ;
2014-03-06 14:54:39 +01:00
drm_panel_disable ( imx_ldb_ch - > panel ) ;
2013-06-18 09:23:57 +08:00
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] )
ldb - > ldb_ctrl & = ~ LDB_CH0_MODE_EN_MASK ;
else if ( imx_ldb_ch = = & ldb - > channel [ 1 ] )
ldb - > ldb_ctrl & = ~ LDB_CH1_MODE_EN_MASK ;
regmap_write ( ldb - > regmap , IOMUXC_GPR2 , ldb - > ldb_ctrl ) ;
if ( ldb - > ldb_ctrl & LDB_SPLIT_MODE_EN ) {
clk_disable_unprepare ( ldb - > clk [ 0 ] ) ;
clk_disable_unprepare ( ldb - > clk [ 1 ] ) ;
}
2014-03-06 14:54:39 +01:00
2014-11-26 13:59:11 +01:00
if ( ldb - > lvds_mux ) {
const struct bus_mux * lvds_mux = NULL ;
if ( imx_ldb_ch = = & ldb - > channel [ 0 ] )
lvds_mux = & ldb - > lvds_mux [ 0 ] ;
else if ( imx_ldb_ch = = & ldb - > channel [ 1 ] )
lvds_mux = & ldb - > lvds_mux [ 1 ] ;
regmap_read ( ldb - > regmap , lvds_mux - > reg , & mux ) ;
mux & = lvds_mux - > mask ;
mux > > = lvds_mux - > shift ;
} else {
mux = ( imx_ldb_ch = = & ldb - > channel [ 0 ] ) ? 0 : 1 ;
}
/* set display clock mux back to original input clock */
ret = clk_set_parent ( ldb - > clk_sel [ mux ] , ldb - > clk_parent [ mux ] ) ;
if ( ret )
dev_err ( ldb - > dev ,
" unable to set di%d parent clock to original parent \n " ,
mux ) ;
2014-03-06 14:54:39 +01:00
drm_panel_unprepare ( imx_ldb_ch - > panel ) ;
2013-06-18 09:23:57 +08:00
}
2016-07-06 14:49:24 +02:00
static int imx_ldb_encoder_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
{
struct imx_crtc_state * imx_crtc_state = to_imx_crtc_state ( crtc_state ) ;
struct imx_ldb_channel * imx_ldb_ch = enc_to_imx_ldb_ch ( encoder ) ;
struct drm_display_info * di = & conn_state - > connector - > display_info ;
u32 bus_format = imx_ldb_ch - > bus_format ;
/* Bus format description in DT overrides connector display info. */
if ( ! bus_format & & di - > num_bus_formats )
bus_format = di - > bus_formats [ 0 ] ;
switch ( bus_format ) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG :
imx_crtc_state - > bus_format = MEDIA_BUS_FMT_RGB666_1X18 ;
break ;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG :
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA :
imx_crtc_state - > bus_format = MEDIA_BUS_FMT_RGB888_1X24 ;
break ;
default :
return - EINVAL ;
}
imx_crtc_state - > di_hsync_pin = 2 ;
imx_crtc_state - > di_vsync_pin = 3 ;
return 0 ;
}
2015-12-15 12:21:09 +01:00
static const struct drm_connector_funcs imx_ldb_connector_funcs = {
2016-07-08 17:41:01 +08:00
. dpms = drm_atomic_helper_connector_dpms ,
2013-06-18 09:23:57 +08:00
. fill_modes = drm_helper_probe_single_connector_modes ,
. detect = imx_ldb_connector_detect ,
2013-11-03 13:30:48 +00:00
. destroy = imx_drm_connector_destroy ,
2016-07-08 17:40:56 +08:00
. reset = drm_atomic_helper_connector_reset ,
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2013-06-18 09:23:57 +08:00
} ;
2015-12-15 12:21:09 +01:00
static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
2013-06-18 09:23:57 +08:00
. get_modes = imx_ldb_connector_get_modes ,
. best_encoder = imx_ldb_connector_best_encoder ,
} ;
2015-12-15 12:21:09 +01:00
static const struct drm_encoder_funcs imx_ldb_encoder_funcs = {
2013-11-03 13:30:48 +00:00
. destroy = imx_drm_encoder_destroy ,
2013-06-18 09:23:57 +08:00
} ;
2015-12-15 12:21:09 +01:00
static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
2013-06-18 09:23:57 +08:00
. mode_set = imx_ldb_encoder_mode_set ,
2016-07-08 17:41:01 +08:00
. enable = imx_ldb_encoder_enable ,
2013-06-18 09:23:57 +08:00
. disable = imx_ldb_encoder_disable ,
2016-07-06 14:49:24 +02:00
. atomic_check = imx_ldb_encoder_atomic_check ,
2013-06-18 09:23:57 +08:00
} ;
static int imx_ldb_get_clk ( struct imx_ldb * ldb , int chno )
{
char clkname [ 16 ] ;
2014-02-28 11:39:42 -03:00
snprintf ( clkname , sizeof ( clkname ) , " di%d " , chno ) ;
2013-06-18 09:23:57 +08:00
ldb - > clk [ chno ] = devm_clk_get ( ldb - > dev , clkname ) ;
if ( IS_ERR ( ldb - > clk [ chno ] ) )
return PTR_ERR ( ldb - > clk [ chno ] ) ;
2014-02-28 11:39:42 -03:00
snprintf ( clkname , sizeof ( clkname ) , " di%d_pll " , chno ) ;
2013-06-18 09:23:57 +08:00
ldb - > clk_pll [ chno ] = devm_clk_get ( ldb - > dev , clkname ) ;
2013-10-23 10:29:55 +03:00
return PTR_ERR_OR_ZERO ( ldb - > clk_pll [ chno ] ) ;
2013-06-18 09:23:57 +08:00
}
2013-11-03 13:30:48 +00:00
static int imx_ldb_register ( struct drm_device * drm ,
struct imx_ldb_channel * imx_ldb_ch )
2013-06-18 09:23:57 +08:00
{
struct imx_ldb * ldb = imx_ldb_ch - > ldb ;
2016-07-06 14:49:24 +02:00
struct drm_encoder * encoder = & imx_ldb_ch - > encoder ;
2013-11-03 13:30:48 +00:00
int ret ;
2016-07-06 14:49:24 +02:00
ret = imx_drm_encoder_parse_of ( drm , encoder , imx_ldb_ch - > child ) ;
2013-11-03 13:30:48 +00:00
if ( ret )
return ret ;
2013-06-18 09:23:57 +08:00
ret = imx_ldb_get_clk ( ldb , imx_ldb_ch - > chno ) ;
if ( ret )
return ret ;
2013-11-03 13:30:48 +00:00
2013-06-18 09:23:57 +08:00
if ( ldb - > ldb_ctrl & LDB_SPLIT_MODE_EN ) {
2013-11-03 13:30:48 +00:00
ret = imx_ldb_get_clk ( ldb , 1 ) ;
2013-06-18 09:23:57 +08:00
if ( ret )
return ret ;
}
2016-07-06 14:49:24 +02:00
drm_encoder_helper_add ( encoder , & imx_ldb_encoder_helper_funcs ) ;
drm_encoder_init ( drm , encoder , & imx_ldb_encoder_funcs ,
DRM_MODE_ENCODER_LVDS , NULL ) ;
2013-06-18 09:23:57 +08:00
drm_connector_helper_add ( & imx_ldb_ch - > connector ,
& imx_ldb_connector_helper_funcs ) ;
2013-11-03 13:30:48 +00:00
drm_connector_init ( drm , & imx_ldb_ch - > connector ,
& imx_ldb_connector_funcs , DRM_MODE_CONNECTOR_LVDS ) ;
2013-06-18 09:23:57 +08:00
2014-03-06 14:54:39 +01:00
if ( imx_ldb_ch - > panel )
drm_panel_attach ( imx_ldb_ch - > panel , & imx_ldb_ch - > connector ) ;
2016-07-06 14:49:24 +02:00
drm_mode_connector_attach_encoder ( & imx_ldb_ch - > connector , encoder ) ;
2013-06-18 09:23:57 +08:00
return 0 ;
}
enum {
LVDS_BIT_MAP_SPWG ,
LVDS_BIT_MAP_JEIDA
} ;
2014-12-02 20:22:49 +01:00
struct imx_ldb_bit_mapping {
u32 bus_format ;
u32 datawidth ;
const char * const mapping ;
2013-06-18 09:23:57 +08:00
} ;
2014-12-02 20:22:49 +01:00
static const struct imx_ldb_bit_mapping imx_ldb_bit_mappings [ ] = {
{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG , 18 , " spwg " } ,
{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG , 24 , " spwg " } ,
{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA , 24 , " jeida " } ,
} ;
static u32 of_get_bus_format ( struct device * dev , struct device_node * np )
2013-06-18 09:23:57 +08:00
{
const char * bm ;
2014-12-02 20:22:49 +01:00
u32 datawidth = 0 ;
2013-06-18 09:23:57 +08:00
int ret , i ;
ret = of_property_read_string ( np , " fsl,data-mapping " , & bm ) ;
if ( ret < 0 )
return ret ;
2014-12-02 20:22:49 +01:00
of_property_read_u32 ( np , " fsl,data-width " , & datawidth ) ;
for ( i = 0 ; i < ARRAY_SIZE ( imx_ldb_bit_mappings ) ; i + + ) {
if ( ! strcasecmp ( bm , imx_ldb_bit_mappings [ i ] . mapping ) & &
datawidth = = imx_ldb_bit_mappings [ i ] . datawidth )
return imx_ldb_bit_mappings [ i ] . bus_format ;
}
dev_err ( dev , " invalid data mapping: %d-bit \" %s \" \n " , datawidth , bm ) ;
2013-06-18 09:23:57 +08:00
2014-12-02 20:22:49 +01:00
return - ENOENT ;
2013-06-18 09:23:57 +08:00
}
static struct bus_mux imx6q_lvds_mux [ 2 ] = {
{
. reg = IOMUXC_GPR3 ,
. shift = 6 ,
. mask = IMX6Q_GPR3_LVDS0_MUX_CTL_MASK ,
} , {
. reg = IOMUXC_GPR3 ,
. shift = 8 ,
. mask = IMX6Q_GPR3_LVDS1_MUX_CTL_MASK ,
}
} ;
/*
* For a device declaring compatible = " fsl,imx6q-ldb " , " fsl,imx53-ldb " ,
* of_match_device will walk through this list and take the first entry
* matching any of its compatible values . Therefore , the more generic
* entries ( in this case fsl , imx53 - ldb ) need to be ordered last .
*/
static const struct of_device_id imx_ldb_dt_ids [ ] = {
{ . compatible = " fsl,imx6q-ldb " , . data = imx6q_lvds_mux , } ,
{ . compatible = " fsl,imx53-ldb " , . data = NULL , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , imx_ldb_dt_ids ) ;
2013-11-03 11:23:34 +00:00
static int imx_ldb_bind ( struct device * dev , struct device * master , void * data )
2013-06-18 09:23:57 +08:00
{
2013-11-03 13:30:48 +00:00
struct drm_device * drm = data ;
2013-11-03 11:23:34 +00:00
struct device_node * np = dev - > of_node ;
2013-06-18 09:23:57 +08:00
const struct of_device_id * of_id =
2013-11-03 11:23:34 +00:00
of_match_device ( imx_ldb_dt_ids , dev ) ;
2013-06-18 09:23:57 +08:00
struct device_node * child ;
const u8 * edidp ;
struct imx_ldb * imx_ldb ;
int dual ;
int ret ;
int i ;
2013-11-03 11:23:34 +00:00
imx_ldb = devm_kzalloc ( dev , sizeof ( * imx_ldb ) , GFP_KERNEL ) ;
2013-06-18 09:23:57 +08:00
if ( ! imx_ldb )
return - ENOMEM ;
imx_ldb - > regmap = syscon_regmap_lookup_by_phandle ( np , " gpr " ) ;
if ( IS_ERR ( imx_ldb - > regmap ) ) {
2013-11-03 11:23:34 +00:00
dev_err ( dev , " failed to get parent regmap \n " ) ;
2013-06-18 09:23:57 +08:00
return PTR_ERR ( imx_ldb - > regmap ) ;
}
2013-11-03 11:23:34 +00:00
imx_ldb - > dev = dev ;
2013-06-18 09:23:57 +08:00
if ( of_id )
imx_ldb - > lvds_mux = of_id - > data ;
dual = of_property_read_bool ( np , " fsl,dual-channel " ) ;
if ( dual )
imx_ldb - > ldb_ctrl | = LDB_SPLIT_MODE_EN ;
/*
2013-07-24 01:05:07 +09:00
* There are three different possible clock mux configurations :
2013-06-18 09:23:57 +08:00
* i . MX53 : ipu1_di0_sel , ipu1_di1_sel
* i . MX6q : ipu1_di0_sel , ipu1_di1_sel , ipu2_di0_sel , ipu2_di1_sel
* i . MX6dl : ipu1_di0_sel , ipu1_di1_sel , lcdif_sel
* Map them all to di0_sel . . . di3_sel .
*/
for ( i = 0 ; i < 4 ; i + + ) {
char clkname [ 16 ] ;
sprintf ( clkname , " di%d_sel " , i ) ;
imx_ldb - > clk_sel [ i ] = devm_clk_get ( imx_ldb - > dev , clkname ) ;
if ( IS_ERR ( imx_ldb - > clk_sel [ i ] ) ) {
ret = PTR_ERR ( imx_ldb - > clk_sel [ i ] ) ;
imx_ldb - > clk_sel [ i ] = NULL ;
break ;
}
2014-11-26 13:59:11 +01:00
imx_ldb - > clk_parent [ i ] = clk_get_parent ( imx_ldb - > clk_sel [ i ] ) ;
2013-06-18 09:23:57 +08:00
}
if ( i = = 0 )
return ret ;
for_each_child_of_node ( np , child ) {
struct imx_ldb_channel * channel ;
2016-04-27 16:23:33 -04:00
struct device_node * ddc_node ;
2016-05-03 14:37:13 +02:00
struct device_node * ep ;
2016-07-08 17:40:58 +08:00
int bus_format ;
2013-06-18 09:23:57 +08:00
ret = of_property_read_u32 ( child , " reg " , & i ) ;
if ( ret | | i < 0 | | i > 1 )
return - EINVAL ;
if ( dual & & i > 0 ) {
2013-11-03 11:23:34 +00:00
dev_warn ( dev , " dual-channel mode, ignoring second output \n " ) ;
2013-06-18 09:23:57 +08:00
continue ;
}
if ( ! of_device_is_available ( child ) )
continue ;
channel = & imx_ldb - > channel [ i ] ;
channel - > ldb = imx_ldb ;
channel - > chno = i ;
2013-11-03 13:30:48 +00:00
channel - > child = child ;
2013-06-18 09:23:57 +08:00
2014-03-06 14:54:39 +01:00
/*
* The output port is port @ 4 with an external 4 - port mux or
* port @ 2 with the internal 2 - port mux .
*/
2016-05-03 14:37:13 +02:00
ep = of_graph_get_endpoint_by_regs ( child ,
imx_ldb - > lvds_mux ? 4 : 2 ,
- 1 ) ;
if ( ep ) {
struct device_node * remote ;
remote = of_graph_get_remote_port_parent ( ep ) ;
of_node_put ( ep ) ;
if ( remote )
channel - > panel = of_drm_find_panel ( remote ) ;
else
return - EPROBE_DEFER ;
of_node_put ( remote ) ;
if ( ! channel - > panel ) {
dev_err ( dev , " panel not found: %s \n " ,
remote - > full_name ) ;
return - EPROBE_DEFER ;
2014-03-06 14:54:39 +01:00
}
}
2016-04-27 16:23:33 -04:00
ddc_node = of_parse_phandle ( child , " ddc-i2c-bus " , 0 ) ;
if ( ddc_node ) {
channel - > ddc = of_find_i2c_adapter_by_node ( ddc_node ) ;
of_node_put ( ddc_node ) ;
if ( ! channel - > ddc ) {
dev_warn ( dev , " failed to get ddc i2c adapter \n " ) ;
return - EPROBE_DEFER ;
}
}
if ( ! channel - > ddc ) {
/* if no DDC available, fallback to hardcoded EDID */
dev_dbg ( dev , " no ddc available \n " ) ;
edidp = of_get_property ( child , " edid " ,
& channel - > edid_len ) ;
if ( edidp ) {
channel - > edid = kmemdup ( edidp ,
channel - > edid_len ,
GFP_KERNEL ) ;
} else if ( ! channel - > panel ) {
/* fallback to display-timings node */
ret = of_get_drm_display_mode ( child ,
& channel - > mode ,
2016-05-24 08:31:49 +02:00
OF_USE_NATIVE_MODE ) ;
2016-04-27 16:23:33 -04:00
if ( ! ret )
channel - > mode_valid = 1 ;
}
2013-06-18 09:23:57 +08:00
}
2016-07-08 17:40:58 +08:00
bus_format = of_get_bus_format ( dev , child ) ;
if ( bus_format = = - EINVAL ) {
2014-12-02 20:22:49 +01:00
/*
* If no bus format was specified in the device tree ,
* we can still get it from the connected panel later .
*/
if ( channel - > panel & & channel - > panel - > funcs & &
channel - > panel - > funcs - > get_modes )
2016-07-08 17:40:58 +08:00
bus_format = 0 ;
2014-12-02 20:22:49 +01:00
}
2016-07-08 17:40:58 +08:00
if ( bus_format < 0 ) {
2014-12-02 20:22:49 +01:00
dev_err ( dev , " could not determine data mapping: %d \n " ,
2016-07-08 17:40:58 +08:00
bus_format ) ;
return bus_format ;
2013-06-18 09:23:57 +08:00
}
2016-07-06 14:49:24 +02:00
channel - > bus_format = bus_format ;
2013-06-18 09:23:57 +08:00
2013-11-03 13:30:48 +00:00
ret = imx_ldb_register ( drm , channel ) ;
2013-06-18 09:23:57 +08:00
if ( ret )
return ret ;
}
2013-11-03 11:23:34 +00:00
dev_set_drvdata ( dev , imx_ldb ) ;
2013-06-18 09:23:57 +08:00
return 0 ;
}
2013-11-03 11:23:34 +00:00
static void imx_ldb_unbind ( struct device * dev , struct device * master ,
void * data )
2013-06-18 09:23:57 +08:00
{
2013-11-03 11:23:34 +00:00
struct imx_ldb * imx_ldb = dev_get_drvdata ( dev ) ;
2013-06-18 09:23:57 +08:00
int i ;
for ( i = 0 ; i < 2 ; i + + ) {
struct imx_ldb_channel * channel = & imx_ldb - > channel [ i ] ;
2014-09-01 18:07:38 +01:00
if ( ! channel - > connector . funcs )
continue ;
2013-11-03 13:30:48 +00:00
channel - > connector . funcs - > destroy ( & channel - > connector ) ;
2016-07-06 14:49:24 +02:00
channel - > encoder . funcs - > destroy ( & channel - > encoder ) ;
2013-12-08 22:03:57 +01:00
kfree ( channel - > edid ) ;
2016-04-27 16:23:33 -04:00
i2c_put_adapter ( channel - > ddc ) ;
2013-06-18 09:23:57 +08:00
}
2013-11-03 11:23:34 +00:00
}
2013-06-18 09:23:57 +08:00
2013-11-03 11:23:34 +00:00
static const struct component_ops imx_ldb_ops = {
. bind = imx_ldb_bind ,
. unbind = imx_ldb_unbind ,
} ;
static int imx_ldb_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & imx_ldb_ops ) ;
}
static int imx_ldb_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & imx_ldb_ops ) ;
2013-06-18 09:23:57 +08:00
return 0 ;
}
static struct platform_driver imx_ldb_driver = {
. probe = imx_ldb_probe ,
. remove = imx_ldb_remove ,
. driver = {
. of_match_table = imx_ldb_dt_ids ,
. name = DRIVER_NAME ,
} ,
} ;
module_platform_driver ( imx_ldb_driver ) ;
MODULE_DESCRIPTION ( " i.MX LVDS driver " ) ;
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;
2013-08-18 21:40:03 -03:00
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;