forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm/loongson: Add GPIO and I2C driver for loongson drm.
Implement use GPIO and I2C driver to detect connector and fetch EDID via DDC. v5: - Use braidge->ddc to get EDID and detect connector. v4: - Delete the gpio_chip subsystem call. - Delete some redundant prints. v3: - Change some driver log to the drm_ version. v2: - Optimize the error handling process. - Delete loongson_i2c_bus_match and loongson_i2c_add function. - Optimize part of the code flow. Signed-off-by: Yi Li <liyi@loongson.cn> Signed-off-by: Chenyang Li <lichenyang@loongson.cn>
- Loading branch information
1 parent
e9a9964
commit 4a5b6ac
Showing
6 changed files
with
266 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
/* | ||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited | ||
*/ | ||
|
||
#include "loongson_drv.h" | ||
#include "loongson_i2c.h" | ||
|
||
static inline void dc_gpio_set_dir(struct loongson_device *ldev, | ||
unsigned int pin, int input) | ||
{ | ||
u32 temp; | ||
|
||
temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_CFG_OFFSET); | ||
if (input) | ||
temp |= 1UL << pin; | ||
else | ||
temp &= ~(1UL << pin); | ||
|
||
ls7a_mm_wreg(ldev, LS7A_DC_GPIO_CFG_OFFSET, temp); | ||
} | ||
|
||
static void dc_gpio_set_val(struct loongson_device *ldev, unsigned int pin, | ||
int high) | ||
{ | ||
u32 temp; | ||
|
||
temp = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_OUT_OFFSET); | ||
if (high) | ||
temp |= 1UL << pin; | ||
else | ||
temp &= ~(1UL << pin); | ||
|
||
ls7a_mm_wreg(ldev, LS7A_DC_GPIO_OUT_OFFSET, temp); | ||
} | ||
|
||
static void loongson_i2c_set_data(void *i2c, int value) | ||
{ | ||
struct loongson_i2c *li2c = i2c; | ||
struct loongson_device *ldev = li2c->ldev; | ||
unsigned int pin = li2c->data; | ||
|
||
if (value) | ||
dc_gpio_set_dir(ldev, pin, 1); | ||
else { | ||
dc_gpio_set_val(ldev, pin, 0); | ||
dc_gpio_set_dir(ldev, pin, 0); | ||
} | ||
} | ||
|
||
static void loongson_i2c_set_clock(void *i2c, int value) | ||
{ | ||
struct loongson_i2c *li2c = i2c; | ||
struct loongson_device *ldev = li2c->ldev; | ||
unsigned int pin = li2c->clock; | ||
|
||
if (value) | ||
dc_gpio_set_dir(ldev, pin, 1); | ||
else { | ||
dc_gpio_set_val(ldev, pin, 0); | ||
dc_gpio_set_dir(ldev, pin, 0); | ||
} | ||
} | ||
|
||
static int loongson_i2c_get_data(void *i2c) | ||
{ | ||
int val; | ||
struct loongson_i2c *li2c = i2c; | ||
struct loongson_device *ldev = li2c->ldev; | ||
unsigned int pin = li2c->data; | ||
|
||
val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET); | ||
|
||
return (val >> pin) & 1; | ||
} | ||
|
||
static int loongson_i2c_get_clock(void *i2c) | ||
{ | ||
int val; | ||
struct loongson_i2c *li2c = i2c; | ||
struct loongson_device *ldev = li2c->ldev; | ||
unsigned int pin = li2c->clock; | ||
|
||
val = ls7a_mm_rreg(ldev, LS7A_DC_GPIO_IN_OFFSET); | ||
|
||
return (val >> pin) & 1; | ||
} | ||
|
||
static int loongson_i2c_create(struct loongson_device *ldev, | ||
struct loongson_i2c *li2c, const char *name) | ||
{ | ||
int ret; | ||
unsigned int i2c_num; | ||
struct drm_device *dev = &ldev->dev; | ||
struct i2c_client *i2c_cli; | ||
struct i2c_adapter *i2c_adapter; | ||
struct i2c_algo_bit_data *i2c_algo_data; | ||
const struct i2c_board_info i2c_info = { | ||
.type = "ddc-dev", | ||
.addr = DDC_ADDR, | ||
.flags = I2C_CLASS_DDC, | ||
}; | ||
|
||
i2c_num = li2c->i2c_id; | ||
i2c_adapter = devm_kzalloc(dev->dev, sizeof(*i2c_adapter), GFP_KERNEL); | ||
if (!i2c_adapter) | ||
return -ENOMEM; | ||
|
||
i2c_algo_data = devm_kzalloc(dev->dev, sizeof(*i2c_algo_data), GFP_KERNEL); | ||
if (!i2c_algo_data) { | ||
ret = -ENOMEM; | ||
goto free_adapter; | ||
} | ||
|
||
i2c_adapter->owner = THIS_MODULE; | ||
i2c_adapter->class = I2C_CLASS_DDC; | ||
i2c_adapter->algo_data = i2c_algo_data; | ||
i2c_adapter->dev.parent = dev->dev; | ||
i2c_adapter->nr = -1; | ||
snprintf(i2c_adapter->name, sizeof(i2c_adapter->name), "%s%d", | ||
name, i2c_num); | ||
|
||
li2c->data = i2c_num * 2; | ||
li2c->clock = i2c_num * 2 + 1; | ||
DRM_INFO("Created i2c-%d, sda=%d, scl=%d\n", | ||
i2c_num, li2c->data, li2c->clock); | ||
|
||
i2c_algo_data->setsda = loongson_i2c_set_data; | ||
i2c_algo_data->setscl = loongson_i2c_set_clock; | ||
i2c_algo_data->getsda = loongson_i2c_get_data; | ||
i2c_algo_data->getscl = loongson_i2c_get_clock; | ||
i2c_algo_data->udelay = DC_I2C_TON; | ||
i2c_algo_data->timeout = usecs_to_jiffies(2200); | ||
|
||
ret = i2c_bit_add_numbered_bus(i2c_adapter); | ||
if (ret) | ||
goto free_algo_data; | ||
|
||
li2c->adapter = i2c_adapter; | ||
i2c_algo_data->data = li2c; | ||
i2c_set_adapdata(li2c->adapter, li2c); | ||
li2c->ldev = ldev; | ||
DRM_INFO("Register i2c algo-bit adapter [%s]\n", i2c_adapter->name); | ||
|
||
i2c_cli = i2c_new_client_device(i2c_adapter, &i2c_info); | ||
if (IS_ERR(i2c_cli)) { | ||
ret = PTR_ERR(i2c_cli); | ||
goto remove_i2c_adapter; | ||
} | ||
|
||
return 0; | ||
|
||
remove_i2c_adapter: | ||
drm_err(dev, "Failed to create i2c client\n"); | ||
i2c_del_adapter(i2c_adapter); | ||
free_algo_data: | ||
drm_err(dev, "Failed to register i2c adapter %s\n", i2c_adapter->name); | ||
kfree(i2c_algo_data); | ||
free_adapter: | ||
kfree(i2c_adapter); | ||
|
||
return ret; | ||
} | ||
|
||
int loongson_dc_gpio_init(struct loongson_device *ldev) | ||
{ | ||
int pin; | ||
|
||
/* set gpio dir output 0-3 */ | ||
for (pin = 0; pin < 4; pin++) { | ||
dc_gpio_set_val(ldev, pin, 0); | ||
dc_gpio_set_dir(ldev, pin, 0); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
int loongson_i2c_init(struct loongson_device *ldev) | ||
{ | ||
int ret; | ||
int i; | ||
|
||
for (i = 0; i < 2; i++) { | ||
ldev->i2c_bus[1].i2c_id = i; | ||
ret = loongson_i2c_create(ldev, &ldev->i2c_bus[i], DC_I2C_NAME); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-only */ | ||
/* | ||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited | ||
*/ | ||
|
||
#ifndef __LOONGSON_I2C_H__ | ||
#define __LOONGSON_I2C_H__ | ||
|
||
#include <linux/i2c.h> | ||
#include <linux/i2c-algo-bit.h> | ||
|
||
#include <drm/drm_edid.h> | ||
|
||
#define DC_I2C_TON 5 | ||
#define DC_I2C_NAME "ls_dc_i2c" | ||
#define DC_MAX_I2C_BUS 2 | ||
|
||
#define LS7A_DC_GPIO_CFG_OFFSET (0x1660) | ||
#define LS7A_DC_GPIO_IN_OFFSET (0x1650) | ||
#define LS7A_DC_GPIO_OUT_OFFSET (0x1650) | ||
|
||
struct loongson_device; | ||
struct loongson_i2c { | ||
struct loongson_device *ldev; | ||
struct i2c_adapter *adapter; | ||
u32 data; | ||
u32 clock; | ||
u32 i2c_id; | ||
}; | ||
|
||
int loongson_i2c_init(struct loongson_device *ldev); | ||
|
||
#endif /* __LOONGSON_I2C_H__ */ |