This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DS90UB925Q-Q1: How to configure ds90ub925 and ds90ub928 to create output in FPD-link III format

Part Number: DS90UB925Q-Q1

I am using DS90UB928 and DS90UB928 to create FPD-link output. 925 is local device.928 is remote device. I use the defualt configuration of 928 and 925.But I can not detect 925 and 928 on the I2C. Should I put 928 and 925 on the same I2C?

Thanks & Regards,

cheng

  • Hi Cheng,

    Can you first establish if there is lock between the two devices and did you set the IDx of the devices correctly? Is it strapped properly according to the D/S?
  • I read the datasheet of DS90UB925 and DS90UB928 and find the ID  could be set by the  resistors of IDX pin. I could know  the ID of 925 and 928 are 0x0c and 0x2c from the board I am using now. I write 0x19 to the 0x00 register of 925, 0x59 to the 0x06 register of 925 and 0x59 to 0x00 register of 928.In devicetree file, I use i2c3 to control  ds90ub925 and ds90ub928 and set  reg=<0x0c> and reg=<0x2c>.When I  probe the device :

    I can just find ds90ub925.When I read register 0x00 of 925: ,there is something wrong.

    SO, could you tell me what to do next.

    Best Regards,

    cheng

  • I have tested some command to read register 0x00,but no one useful.

     dra7xx-evm login: root
    root@dra7xx-evm:~# i2cdetect -y -r 2
         0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- UU -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    70: -- -- -- -- -- -- -- --                         
    root@dra7xx-evm:~# i2cdump -f -y 2 0x0c
    i2cdump: block read failed
    root@dra7xx-evm:~# i2cset -y 2 0x0c 00
    i2cset: can't set address to 0x0c: Device or resource busy
    root@dra7xx-evm:~# i2cset -y -f 2 0x0c 00
    i2cset: write failed: Remote I/O error
    root@dra7xx-evm:~# i2cget -y 2 0x0c 0    
    i2cget: can't set address to 0x0c: Device or resource busy
    root@dra7xx-evm:~# i2cget -f -y 2 0x0c 0 
    i2cget: read failed: Remote I/O error
    I  have measured the voltage of PDB PIN, SCL PIN and SDA PIN , they are all normal.
    Thanks and Regards,
    cheng
     
  • 1.Make sure all your hardware connections are correct. This means please check that all your pin configurations are correct according to the datasheet
    2. Check your MODE configuration for the pins (in table 4 of 925 and table 6 of 928). Make sure the LF mode of 925 and 928 are in the compatible mode. Also in the 928, make sure 0x01 [2] is enabled.
    3. In the 925, program your IDx address. Should be 0x0C based on your message. (Make this the local device for time being)
    4. In the 928, program your IDx address. Should be 0x0C based on your message.(Make this local device for time being)
    5. Using 925 as local device, enable I2C pass through mode (0x03 [3] = 1) & (0x17 [7] = 1)
    6. Check for link detect (read register 0x0C [0] =1)
    7. Read the DES Device ID (0x06)

    Please tell me your results when your try this.
  • 1.I looked the PIN Descriptions of datasheet, and I am sure all the pin configurations are correct.

    2.The LFMODE of 925 and 928 are both at 15-85MHZ. I have set 928:0x01=0x04;

    3. 925:0x00=0x18

    4. 928:0x00=0x58

    5. I read  the register 0x0c and 0x06 of 925, the results are 0xffffff87 and 0xffffff87. They are not right.

    When I read the registers after entering the system, I still can not get value:

    The board I am using now is based on TAD2XX. The SDK I am using is PROCESSOR_SDK_VISION_03_06_00_00_setuplinux.

    I write and read register in the file: fpd3_serders.c

    /*
     * fpd3_serdes.c
     *
     * lvds based serial link interface for onboard communication.
     * Copyright (C) 2014-2015 Texas Instruments Incorporated - http://www.ti.com/
     * Authors: Nikhil Devshatwar <nikhil.nd@ti.com>
     *
     * This program is free software; you can redistribute it and/or modify it
     * under the terms of the GNU General Public License version 2 as published by
     * the Free Software Foundation.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     * more details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    /*
     * Datasheets:-
     * DS90UB913aq/DS90UB914aq http://www.ti.com/lit/ds/snls443a/snls443a.pdf
     * DS90UH925Q http://www.ti.com/lit/ds/symlink/ds90uh925q-q1.pdf
     * DS90UH928Q http://www.ti.com/lit/ds/snls440a/snls440a.pdf
     * DS90UB924Q http://www.ti.com/lit/ds/snls512/snls512.pdf
     * DS90UB921Q http://www.ti.com/lit/ds/symlink/ds90ub921-q1.pdf
     *
     * Documentation:-
     * -> Documentation/video-serdes.txt
     * -> Documentation/devicetree/bindings/video/fpd3-serdes.txt
     */
    
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/i2c.h>
    #include <linux/gpio.h>
    #include <linux/delay.h>
    #include <linux/of_gpio.h>
    #include <linux/of_device.h>
    #include "fpd3_serdes.h"
    
    static const unsigned int fpd3_12bit_ser_init[] = {
    	/* auto volt ctrl en, 3.3V, digital reset0, digital reset1 */
    	FPD3_SER_RESET,		0x33,
    	/* RX CRC check, TX parity en, i2c passthrough, rising edge pclk */
    	FPD3_SER_CONFIG1,	0xc5,
    	/* GPIO0 - o/p LOW, GPIO0 - o/p HIGH */
    	FPD3_SER_GPIO_01,	0x55,
    };
    
    static const unsigned int fpd3_12bit_des_init[] = {
    	/* back channel en */
    	FPD3_DES_RESET,		0x04,
    	/* RX parity check, TX CRC check, auto volt ctrl en
    	 * i2c passthrough, auto ack WR, rising edge pclk */
    	FPD3_DES_CONFIG1,	0xec,
    };
    
    static const unsigned int fpd3_24bit_ser_init[] = {
    	/* digital reset1 */
    	FPD3_SER_RESET,		0x02,
    	/* back chan en, auto ack WR, i2c passthrough, rising edge pclk */
    	FPD3_SER_CONFIG1,	0xab,
    	/* failsafe low, LFMODE override, high freq*/
    	FPD3_SER_CONFIG2,	0x8a,
    	/* RGB data, 24bit, i2s data forwarding */
    	FPD3_SER_DATA_CTRL,	0x8a,
    };
    
    static const unsigned int fpd3_24bit_des_init[] = {
    	/* digital reset1 */
    	FPD3_DES_RESET,		0x02,
    	/* back channel en */
    	FPD3_DES_RESET,		0x04,
    	/* auto clk en, LFMODE override, high freq */
    	FPD3_DES_CONFIG0,	0x2a,
    	/* failsafe pull up, i2c passthrough, auto ack */
    	FPD3_DES_CONFIG1,	0x4c,
    };
    
    static const unsigned int fpd3_921_ser_init[] = {
    	/* Reset entire digital block except registers */
    	FPD3_SER_RESET,		0x01,
    	/* back chan en, auto ack WR, i2c passthrough, rising edge pclk */
    	FPD3_SER_CONFIG1,	0x8b,
    };
    
    static const unsigned int fpd3_924_des_init[] = {
    	/* digital reset1 */
    	FPD3_DES_RESET,		0x02,
    	/* back channel en */
    	FPD3_DES_RESET,		0x04,
    };
    
    static struct fpd3_serdes_platform_data fpd3_serdes_pdata[] = {
    	{
    		.name = "fpd3_12b_ser",
    		.dev_type = FPD3_SER_DEV,
    		.ngpio = 4,
    		.nslaves = 1,
    		.device_id = 0xb0,
    		.gpio_2reg = 2 * FPD3_SER_GPIO_01,
    		.init_seq = fpd3_12bit_ser_init,
    		.init_len = ARRAY_SIZE(fpd3_12bit_ser_init),
    	},
    	{
    		.name = "fpd3_12b_des",
    		.dev_type = FPD3_DES_DEV,
    		.ngpio = 4,
    		.nslaves = 8,
    		.device_id = 0xc0,
    		.gpio_2reg = 2 * FPD3_DES_GPIO_01,
    		.init_seq = fpd3_12bit_des_init,
    		.init_len = ARRAY_SIZE(fpd3_12bit_des_init),
    	},
    	{
    		.name = "fpd3_24b_ser",
    		.dev_type = FPD3_SER_DEV,
    		.ngpio = 4,
    		.nslaves = 1,
    		.device_id = 0x36,
    		.gpio_2reg = 2 * FPD3_SER_GPIO_01 + 1,
    		.init_seq = fpd3_24bit_ser_init,
    		.init_len = ARRAY_SIZE(fpd3_24bit_ser_init),
    	},
    	{
    		.name = "fpd3_24b_des",
    		.dev_type = FPD3_DES_DEV,
    		.ngpio = 4,
    		.nslaves = 8,
    		.device_id = 0x58,
    		.gpio_2reg = 2 * FPD3_DES_GPIO_01 + 1,
    		.init_seq = fpd3_24bit_des_init,
    		.init_len = ARRAY_SIZE(fpd3_24bit_des_init),
    	},
    	{
    		.name = "fpd3_924_des",
    		.dev_type = FPD3_DES_DEV,
    		.ngpio = 4,
    		.nslaves = 8,
    		.device_id = 0x58,
    		.gpio_2reg = 2 * FPD3_DES_GPIO_01 + 1,
    		.init_seq = fpd3_924_des_init,
    		.init_len = ARRAY_SIZE(fpd3_924_des_init),
    	},
    	{
    		.name = "fpd3_921_ser",
    		.dev_type = FPD3_SER_DEV,
    		.ngpio = 4,
    		.nslaves = 1,
    		.device_id = 0x18,
    		.gpio_2reg = 2 * FPD3_SER_GPIO_01 + 1,
    		.init_seq = fpd3_921_ser_init,
    		.init_len = ARRAY_SIZE(fpd3_921_ser_init),
    	},
    };
    
    /* GPIO operations */
    static int __fpd3_serdes_dirval(struct gpio_chip *chip,
    			unsigned offset, int value, int shift)
    {
    	dev_err(&chip->dev, "FC __fpd3_serdes_dirval");
    	struct fpd3_serdes_data *data =
    			container_of(chip, struct fpd3_serdes_data, chip);
    	const struct fpd3_serdes_platform_data *pdata = data->pdata;
    	struct i2c_client *client = data->client;
    	int reg, val = 0x00;
    
    	reg = (pdata->gpio_2reg + offset) / 2;
    	if ((pdata->gpio_2reg + offset) % 2)
    		shift += 4;
    
    	val = i2c_read_le8(client, reg);
    
    	if (value)
    		val |= 1 << shift;
    	else
    		val &= ~(1 << shift);
    
    	return i2c_write_le8(client, reg, val);
    }
    
    static int fpd3_serdes_input(struct gpio_chip *chip, unsigned offset)
    {
    	dev_err(&chip->dev, "FC fpd3_serdes_input");
    	return __fpd3_serdes_dirval(chip, offset,
    			GPIO_DIR_INPUT, GPIO_SHIFT_DIR);
    }
    static int fpd3_serdes_output(struct gpio_chip *chip,
    		unsigned offset, int value)
    {
    	dev_err(&chip->dev, "FC fpd3_serdes_output");
    	int ret;
    
    	ret = __fpd3_serdes_dirval(chip, offset,
    			GPIO_DIR_OUTPUT, GPIO_SHIFT_DIR);
    
    	if (ret)
    		ret = __fpd3_serdes_dirval(chip, offset,
    			value, GPIO_SHIFT_VAL);
    
    	return ret;
    }
    
    static void fpd3_serdes_set(struct gpio_chip *chip, unsigned offset, int value)
    {
    	dev_err(&chip->dev, "FC fpd3_serdes_set");
    	__fpd3_serdes_dirval(chip, offset, value, GPIO_SHIFT_VAL);
    }
    
    static int fpd3_serdes_get(struct gpio_chip *chip, unsigned offset)
    {
    	dev_err(&chip->dev, "FC fpd3_serdes_get");
    	struct fpd3_serdes_data *data =
    			container_of(chip, struct fpd3_serdes_data, chip);
    	const struct fpd3_serdes_platform_data *pdata = data->pdata;
    	struct i2c_client *client = data->client;
    	int reg;
    
    	int shift = GPIO_SHIFT_VAL;
    	reg = (pdata->gpio_2reg + offset) / 2;
    	if ((pdata->gpio_2reg + offset) % 2)
    		shift += 4;
    
    	return (i2c_read_le8(client, reg) | 1 << shift) ? 1 : 0;
    }
    
    static struct gpio_chip fpd3_serdes_gpiochip = {
    	.owner			= THIS_MODULE,
    	.base			= -1,
    	.ngpio			= 4,
    	.can_sleep		= false,
    
    	.get			= fpd3_serdes_get,
    	.set			= fpd3_serdes_set,
    	.direction_input	= fpd3_serdes_input,
    	.direction_output	= fpd3_serdes_output,
    };
    
    int fpd3_serdes_initialize(struct i2c_client *client)
    {
    	dev_err(&client->dev, "FC fpd3_serdes_initialize");
    	struct fpd3_serdes_data *data = i2c_get_clientdata(client);
    	const struct fpd3_serdes_platform_data *pdata = data->pdata;
    	const unsigned int *seq;
    	int i, len, reg, ret = 0, count = 0;
    
    	if (data->slave_mode == false) {
    		/* Wait for the device to initialize and show up device ID */
    		reg = pdata->dev_type == FPD3_SER_DEV ?
    			FPD3_SER_DEV_ID : FPD3_DES_DEV_ID;//chengbo
    		dev_err(&client->dev, "fpd3_serdes_initialize  reg DEV_ID 0x%2x ", reg);
    		while (ret != pdata->device_id && count < FPD3_MAX_POLL_COUNT) {
    			ret = i2c_read_le8(client, reg);
    			dev_err(&client->dev,"fpd3_serdes_initialize pdata->device_id 0x%2x",pdata->device_id);
    			dev_err(&client->dev, "fpd3_serdes_initialize read ret from reg:  reg 0x%2x ret 0x%02x",
    			reg, ret);
    			count++;
    			mdelay(3);
    		}
    		if (
    		== FPD3_MAX_POLL_COUNT) {
    			dev_err(&client->dev, "Not a %s chip", pdata->name);
    			return -ENODEV;
    		}
    	}
    
    	len = pdata->init_len;
    	seq = pdata->init_seq;
    	for (i = 0; i < len; i += 2) {
    		ret = i2c_write_le8(client, seq[i], seq[i+1]);
    		if (ret)
    			break;
    		if (seq[i] == 0x01)
    			udelay(500);
    		else
    			udelay(10);
    	}
    
    	return ret;
    }
    EXPORT_SYMBOL(fpd3_serdes_initialize);
    
    /* On Vision app board (up to Rev B), the deserializer I2C addresses
     * conflicts with the HDMI receiver chip, making deserializer unaccessible.
     * The workaround is to change the I2C address of the HDMI receiver at runtime.
     * Following code removes the conflict between HDMI SIL9127 and deserializers
     * 0x31 is default I2C address of SIL9127
     */
    static int fpd3_serdes_remove_conflict(struct i2c_client *client)
    {
    	dev_err(&client->dev, "FC fpd3_serdes_remove_conflict");
    	struct regval {
    		u8	reg;
    	u8	val;
    	} conflict_regval[] = {
    		{	0x14,	0xf4,	},
    		{	0x15,	0xf6,	},
    		{	0x16,	0xf8,	},
    		{	0x17,	0xfa,	},
    		{	0x18,	0xfc,	},
    		{	0x19,	0xfe,	},
    	};
    	union i2c_smbus_data data;
    	int i, ret;
    	u16 addr = 0x31;
    	static bool conflict_exist = true;
    
    	if (!conflict_exist)
    		return 0;
    
    	conflict_exist = false;
    	for (i = 0; i < 6; i++) {
    		data.byte = conflict_regval[i].val;
    		ret = i2c_smbus_xfer(client->adapter, addr, client->flags,
    			I2C_SMBUS_WRITE, conflict_regval[i].reg,
    			I2C_SMBUS_BYTE_DATA, &data);
    		if (ret)
    			return ret;
    	}
    
    	dev_err(&client->dev, "Removed conflict b/w HDMI SIL");
    	return 0;
    }
    //引脚是否有效,PDB是否需要上拉;是否有gpio-controller和slave-mode
    static int fpd3_serdes_of_probe(struct i2c_client *client,
    			struct device_node *node)
    {
    	dev_err(&client->dev, "FC fpd3_serdes_of_probe");
    	struct fpd3_serdes_data *data = i2c_get_clientdata(client);
    	int gpio, ret;
    
    	gpio = of_get_gpio(node, 0);
    	if (gpio_is_valid(gpio)) {
    		/* Toggle the pdb bit from LOW to HIGH */
    		ret = devm_gpio_request_one(&client->dev, gpio,
    				GPIOF_INIT_HIGH, "des_pdb");
    	} else if (gpio == -ENOENT)
    		/* Entry not present - no need to toggle pdb */
    		ret = 0;
    	else
    		ret = gpio;
    
    	if (ret)
    		return ret;
    
    	data->gpio_export = of_find_property(node, "gpio-controller", NULL) ?
    			true : false;
    	data->slave_mode = of_find_property(node, "slave-mode", NULL) ?
    			true : false;
    	return 0;
    }
    
    static const struct i2c_device_id fpd3_serdes_i2c_ids[] = {
    	{ "ds90ub913aq", 8 },
    	{ "ds90ub914aq", 8 },
    	{ "ds90ub925q", 8 },
    	{ "ds90ub928q", 8 },
    	{ "ds90ub924q", 8 },
    	{ "ds90ub921q", 8 },
    	{ }
    };
    MODULE_DEVICE_TABLE(i2c, fpd3_serdes_i2c_ids);
    
    static const struct of_device_id fpd3_serdes_dt_ids[] = {
    	{.compatible = "ti,ds90ub913aq", &fpd3_serdes_pdata[0], },
    	{.compatible = "ti,ds90ub914aq", &fpd3_serdes_pdata[1], },
    	{.compatible = "ti,ds90ub925q", &fpd3_serdes_pdata[2], },
    	{.compatible = "ti,ds90ub928q", &fpd3_serdes_pdata[3], },
    	{.compatible = "ti,ds90ub924q", &fpd3_serdes_pdata[4], },
    	{.compatible = "ti,ds90ub921q", &fpd3_serdes_pdata[5], },
    	{ }
    };
    
    MODULE_DEVICE_TABLE(of, fpd3_serdes_dt_ids);
    
    static int fpd3_serdes_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
        dev_err(&client->dev, "FC fpd3_serdes_probe");
    	const struct of_device_id *of_dev_id;
    	const struct fpd3_serdes_platform_data *pdata;
    	struct fpd3_serdes_data *data;
    	int status;
    
    	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
    	if (!data)
    		return -ENOMEM;
    
    	data->client = client;
    	i2c_set_clientdata(client, data);
    
    	fpd3_serdes_remove_conflict(client);
    
    	of_dev_id = of_match_device(fpd3_serdes_dt_ids, &client->dev);
    	if (!of_dev_id) {
    		dev_err(&client->dev, "Unable to match device");
    		return -EINVAL;
    	}
    	pdata = of_dev_id->data;
    	data->pdata = pdata;
    
    	status = fpd3_serdes_of_probe(client, client->dev.of_node);
    	if (status)
    		goto fail;
    
    	if (data->slave_mode == false) {
    		status = fpd3_register_i2c_adapter(client);
    		if (status) {
    			dev_err(&client->dev, "Cannot register i2c adapter");
    			goto fail;
    		}
    	} else {
    		status = fpd3_serdes_initialize(client);
    		if (status)
    			goto fail;
    	}
    
    	if (data->gpio_export == true) {
    		/* Register local gpios as a separate gpiochip */
    		data->chip = fpd3_serdes_gpiochip;
    		data->chip.ngpio = pdata->ngpio;
    		data->chip.dev = &client->dev;
    		data->chip.label = client->name;
    		status = gpiochip_add(&data->chip);
    		if (status)
    			goto fail;
    	}
    
    	if (status)
    		goto unreg_gpio;
    
    	dev_err(&client->dev, "%s %s ready", pdata->dev_type == FPD3_SER_DEV ?
    		"Serializer" : "Deserializer", data->pdata->name);
    
    	return 0;
    
    unreg_gpio:
    	if (data->gpio_export == true) {
    		gpiochip_remove(&data->chip);
    		return -1;
    	}
    fail:
    	return status;
    }
    
    static int fpd3_serdes_remove(struct i2c_client *client)
    {
    	dev_err(&client->dev, "FC fpd3_serdes_remove");
    	struct fpd3_serdes_data	*data = i2c_get_clientdata(client);
    	kfree(data);
    
    	return 0;
    }
    
    static struct i2c_driver fpd3_serdes_driver = {
    	.driver = {
    		.name = "fpd3_serdes",
    		.owner = THIS_MODULE,
    		.of_match_table	= fpd3_serdes_dt_ids,
    	},
    	.probe = fpd3_serdes_probe,
    	.remove = fpd3_serdes_remove,
    	.id_table = fpd3_serdes_i2c_ids
    };
    module_i2c_driver(fpd3_serdes_driver);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Nikhil Devshatwar");
    

    Is that right??If not, could you give me a sample to write and read register in this board.

  • It looks like you still didn't enable i2c pass through, (0x03 [3] = 1) & (0x17 [7] = 1) which may be affecting your idx read. As for SDK, you will need to post to their forum to get help from the processor side.
  • well , I will post this question to SDK forum. And could you give me an I2C example code that is common for all the board to write and read registers .
    Thanks and Regards,
    cheng
  • Hi Chengbo,

    Sorry for delay. In the 954 D/S, we have an example on i2c broaddcast. It enables i2c pass through and reads the SER idx. You will need to determine the correct register for your 925.

    # "FPD3_PORT_SEL Broaddcast RX0/1"
    WriteI2C(0x4c,0x0f) # RX_PORT0 read; RX0/1 write
    # "enable pass through"
    WriteI2C(0x58,0x58) # enable pass through
    WriteI2C(0x5c,0x18) # "SER_ALIAS_ID"
    WriteI2C(0x5d,0x60) # "SlaveID[0]"
    WriteI2C(0x65,0x60) # "SlaveAlias[0]"
  • import time
    #reset and initialize sensor from DES using 953

    # Set up IDs
    UB954 = 0x7A
    UB953ID = 0x30
    UB953 = 0x18
    SensorID = 0x60
    Sensor = 0xC4

    # Set up Port0
    board.WriteI2C(UB954, 0x4C, 0x01)

    # Set up Back Channel Config (0x58)
    board.WriteI2C(UB954,0x58,0x5E)

    # Set up SER ID
    #board.WriteI2C(UB954,0x5B,UB953ID)
    # Set up SER Alias ID
    board.WriteI2C(UB954,0x5C,UB953)
    # Set up Slave/Camera ID
    board.WriteI2C(UB954,0x5D,SensorID)
    # Set up Slave/Camera Alias ID
    board.WriteI2C(UB954,0x65,Sensor)


    Here is another example code for the 954/953 sensor set up (I just took the beginning part where they set up the address in the 954).
  • Hello Cheng,

    Did you have anymore questions ?
  • Your answer is very useful.
    Thanks & Regards,

    cheng