/* * DS90UB947-Q1 1080p OpenLDS to FPD-Link III Serializer driver * * Copyright (C) 2016-2018, NVIDIA Corporation. All rights reserved. * * 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; version 2 of the License. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "dsi.h" #include "ds90ub947-q1.h" /* TODO: support multiple instances */ struct ds90ub947_data *g_ds90ub947_data; static void ds90ub947_lvds2fpdl_en_gpio(struct ds90ub947_data *lvds2fpdl, bool enable) { if (!gpio_is_valid(lvds2fpdl->en_gpio)) return; if (enable) { gpio_direction_output(lvds2fpdl->en_gpio, !(lvds2fpdl->en_gpio_flags & OF_GPIO_ACTIVE_LOW)); /* Wait > 1.5ms before accessing I2C */ usleep_range(1500, 2000); } else { gpio_direction_output(lvds2fpdl->en_gpio, lvds2fpdl->en_gpio_flags & OF_GPIO_ACTIVE_LOW); } } bool ds90ub947_lvds2fpdlink3_detect(struct tegra_dc *dc) { int ret, val; if (g_ds90ub947_data == NULL) return false; mutex_lock(&g_ds90ub947_data->lock); ret = regmap_read(g_ds90ub947_data->regmap, DS90UB947_GENERAL_STATUS, &val); mutex_unlock(&g_ds90ub947_data->lock); if (ret == 0 && (val & 0x01)) return true; return false; } static int ds90ub947_init(struct ds90ub947_data *data) { int i; unsigned int val, reg, mask; int ret = 0; mutex_lock(&data->lock); if (gpio_is_valid(data->en_gpio)) { ret = devm_gpio_request_one(&data->client->dev, data->en_gpio, data->en_gpio_flags & OF_GPIO_ACTIVE_LOW, "lvds2fpdl"); if (ret) pr_err("%d: lvds2fpdl GPIO request failed\n", ret); } else { pr_err("%d: lvds2fpdl GPIO is invalid\n", ret); } ds90ub947_lvds2fpdl_en_gpio(data, true); /* set backchanel crc setting */ /*setting not needed since it is enabled by default */ /* set backchannel i2c setting */ ret = regmap_update_bits(data->regmap, DS90UB947_GENERAL_CONFIG, BIT(DS90UB947_I2C_PASSTHROUGH_BIT), data->en_bchnl_i2c_passthrough << DS90UB947_I2C_PASSTHROUGH_BIT); if (ret) goto fault; ret = regmap_update_bits(data->regmap, DS90UB947_I2C_CONTROL, BIT(DS90UB947_I2C_PASSALL_BIT), data->en_bchnl_i2c_passthrough << DS90UB947_I2C_PASSALL_BIT); if (ret) goto fault; /* set backchannel irq settings */ ret = regmap_update_bits(data->regmap, DS90UB947_INTR_CNTRL_REG, BIT(DS90UB947_INTR_ENABLE_BIT) | BIT(DS90UB947_INTR_RX_ENABLE_BIT), data->en_bchnl_irq << DS90UB947_INTR_ENABLE_BIT | data->en_bchnl_irq << DS90UB947_INTR_RX_ENABLE_BIT); if (ret) goto fault; /* set deserializer address */ ret = regmap_update_bits(data->regmap, DS90UB947_DESER_ADDR, 0xFF << DS90UB947_DES_ID_SHIFT_WIDTH, data->deser_address << DS90UB947_DES_ID_SHIFT_WIDTH); if (ret) goto fault; /* set slave address */ ret = regmap_update_bits(data->regmap, DS90UB947_SLAVE_ADDR, 0xFF << DS90UB947_SLAVE_ADDR_SHIFT_WIDTH, data->slave_address << DS90UB947_SLAVE_ADDR_SHIFT_WIDTH); if (ret) goto fault; /* set slave alias address*/ ret = regmap_update_bits(data->regmap, DS90UB947_SLAVE_ALIAS, 0xFF << DS90UB947_SLAVE_ALIAS_SHIFT_WIDTH, data->slave_alias_address << DS90UB947_SLAVE_ALIAS_SHIFT_WIDTH); if (ret) goto fault; for (i = 0; i < data->n_init_regs; i += 3) { reg = data->init_regs[i]; mask = data->init_regs[i + 1]; val = data->init_regs[i + 2]; ret = regmap_update_bits(data->regmap, reg, mask, val); pr_info("%s: regmap_update_bits returned %d\n", __func__, ret); if (ret) { dev_err(&data->client->dev, "failed to write to reg %#x\n", reg); break; } } fault: mutex_unlock(&data->lock); return ret; } #ifdef CONFIG_DEBUG_FS static int ds90ub947_init_regs_show(struct seq_file *s, void *unused) { struct ds90ub947_data *data = s->private; int i, ret; unsigned int val, reg, mask, cval; mutex_lock(&data->lock); seq_printf(s, "%13s%13s%13s%13s\n", "reg", "mask", "init val", "current val"); for (i = 0; i < data->n_init_regs; i += 3) { reg = data->init_regs[i]; mask = data->init_regs[i + 1]; val = data->init_regs[i + 2]; ret = regmap_read(data->regmap, reg, &cval); if (ret) seq_printf(s, "%#13x%#13x%#13x%13s\n", reg, mask, val, "read error"); else seq_printf(s, "%#13x%#13x%#13x%#13x\n", reg, mask, val, cval); } mutex_unlock(&data->lock); return 0; } static int ds90ub947_init_regs_open(struct inode *inode, struct file *file) { return single_open(file, ds90ub947_init_regs_show, inode->i_private); } static const struct file_operations init_regs_fops = { .open = ds90ub947_init_regs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static void ds90ub947_panel_remove_debugfs(struct ds90ub947_data *data) { /* debugfs_remove_recursive(NULL) is safe */ debugfs_remove_recursive(data->debugdir); data->debugdir = NULL; } static void ds90ub947_panel_create_debugfs(struct ds90ub947_data *data) { struct dentry *pEntry; data->debugdir = debugfs_create_dir(DEV_NAME, NULL); pr_debug("%s: data->debugdir = %p\n", __func__, data->debugdir); if (!data->debugdir) goto err; pEntry = debugfs_create_file("init_regs", 0644, data->debugdir, data, &init_regs_fops); pr_debug("%s: debugfs_create_file returned %p\n", __func__, pEntry); if (pEntry == NULL) goto err; return; err: ds90ub947_panel_remove_debugfs(data); dev_err(&data->client->dev, "Failed to create debugfs\n"); } #else static void ds90ub947_panel_create_debugfs(struct ds90ub947_data *data) { } static void ds90ub947_panel_remove_debugfs(struct ds90ub947_data *data) { } #endif static int of_ds90ub947_parse_pdata(struct i2c_client *client, struct ds90ub947_data *data) { struct device_node *np = client->dev.of_node; int count = 0; int i = 0; struct property *prop; const __be32 *p; u32 u, temp; u32 *init_regs; enum of_gpio_flags flags; data->en_gpio = of_get_named_gpio_flags(np, "ti,enable-gpio", 0, &flags); data->en_gpio_flags = flags; if (data->en_gpio < 0) dev_err(&client->dev, "lvds2fpdl: gpio number not provided\n"); else if (!gpio_is_valid(data->en_gpio)) dev_err(&client->dev, "lvds2fpdl: en gpio is invalid\n"); /* * init-regs is an array of groups of 3 values: reg address, mask, * and value to write */ of_property_for_each_u32(np, "init-regs", prop, p, u) count++; if (!count) return 0; if ((count % 3) != 0) { dev_err(&data->client->dev, "invalid \"init-regs\" data\n"); return -EINVAL; } init_regs = devm_kzalloc(&client->dev, count * sizeof(u), GFP_KERNEL); if (!init_regs) { dev_err(&client->dev, "Failed to allocate init_regs\n"); return -ENOMEM; } of_property_for_each_u32(np, "init-regs", prop, p, u) init_regs[i++] = u & 0xff; data->init_regs = init_regs; data->n_init_regs = count; if (!of_property_read_u32(np, "ti,enable-bchnl-i2c", &temp)) data->en_bchnl_i2c_passthrough = temp; else data->en_bchnl_i2c_passthrough = 0; if (!of_property_read_u32(np, "ti,enable-bchnl-irq", &temp)) data->en_bchnl_irq = temp; else data->en_bchnl_irq = 0; if (!of_property_read_u32(np, "ti,deser-addr", &temp)) data->deser_address = temp; else data->deser_address = 0; if (!of_property_read_u32(np, "ti,slave-addr", &temp)) data->slave_address = temp; else data->slave_address = 0; if (!of_property_read_u32(np, "ti,slave-alias-addr", &temp)) data->slave_alias_address = temp; else data->slave_alias_address = 0; return 0; } static int ds90ub947_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = client->adapter; struct ds90ub947_data *data; struct regmap_config rconfig; int ret; pr_info("%s: entered\n", __func__); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "i2c functionality check fail.\n"); ret = -ENODEV; goto err1; } data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; goto err1; } g_ds90ub947_data = data; data->client = client; ret = of_ds90ub947_parse_pdata(client, data); if (ret) { dev_err(&client->dev, "of data parse failed\n"); goto err2; } memset(&rconfig, 0, sizeof(rconfig)); rconfig.reg_bits = 8; rconfig.val_bits = 8; rconfig.cache_type = REGCACHE_NONE; data->regmap = regmap_init_i2c(client, &rconfig); if (!data->regmap) { dev_err(&client->dev, "Failed to allocate register map\n"); ret = -ENOMEM; goto err2; } mutex_init(&data->lock); ret = ds90ub947_init(data); if (ret) { dev_err(&client->dev, "initializing registers failed\n"); goto err3; } ds90ub947_panel_create_debugfs(data); pr_info("%s: returning %d\n", __func__, ret); return ret; err3: mutex_destroy(&data->lock); err2: if (data->init_regs) devm_kfree(&client->dev, data->init_regs); g_ds90ub947_data = NULL; devm_kfree(&client->dev, data); err1: pr_info("%s: returning %d\n", __func__, ret); return ret; } static int ds90ub947_remove(struct i2c_client *client) { struct ds90ub947_data *data = i2c_get_clientdata(client); ds90ub947_panel_remove_debugfs(data); ds90ub947_lvds2fpdl_en_gpio(data, false); devm_gpio_free(&client->dev, data->en_gpio); mutex_destroy(&data->lock); devm_kfree(&client->dev, data->init_regs); devm_kfree(&client->dev, data); return 0; } static const struct i2c_device_id ds90ub947_id[] = { {DEV_NAME, 0}, { } }; MODULE_DEVICE_TABLE(i2c, ds90ub947_id); static const struct of_device_id ds90ub947_of_match[] = { {.compatible = "ti,ds90ub947-q1", }, { }, }; static struct i2c_driver ds90ub947_driver = { .driver = { .name = DEV_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(ds90ub947_of_match), }, .probe = ds90ub947_probe, .remove = ds90ub947_remove, .id_table = ds90ub947_id, }; module_i2c_driver(ds90ub947_driver); MODULE_AUTHOR("Daniel Solomon "); MODULE_DESCRIPTION("DS90UB947-Q1 1080p OpenLDS to FPD-Link III Serializer driver"); MODULE_LICENSE("GPL");