Unverified Commit b0c20d82 authored by Marcus Folkesson's avatar Marcus Folkesson Committed by Javier Martinez Canillas
Browse files

drm/sitronix/st7571-i2c: make probe independent of hw interface



Create an interface independent layer for the probe function. This is to
make it possible to add support for other interfaces.

Reviewed-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
Signed-off-by: default avatarMarcus Folkesson <marcus.folkesson@gmail.com>
Link: https://patch.msgid.link/20251215-st7571-split-v3-4-d5f3205c3138@gmail.com


Signed-off-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
parent d93a4354
Loading
Loading
Loading
Loading
+112 −53
Original line number Diff line number Diff line
@@ -80,6 +80,33 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0

struct st7571_i2c_transport {
	struct i2c_client *client;

	/*
	 * Depending on the hardware design, the acknowledge signal may be hard to
	 * recognize as a valid logic "0" level.
	 * Therefor, ignore NAK if possible to stay compatible with most hardware designs
	 * and off-the-shelf panels out there.
	 *
	 * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
	 *
	 * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
	 * I2C interface compatible.
	 * Separating acknowledge-output from serial data
	 * input is advantageous for chip-on-glass (COG) applications. In COG
	 * applications, the ITO resistance and the pull-up resistor will form a
	 * voltage  divider, which affects acknowledge-signal level. Larger ITO
	 * resistance will raise the acknowledged-signal level and system cannot
	 * recognize this level as a valid logic “0” level. By separating SDA_IN from
	 * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
	 * For applications which check acknowledge-bit, it is necessary to minimize
	 * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
	 *
	 */
	bool ignore_nak;
};

static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)
{
	return container_of(drm, struct st7571_device, drm);
@@ -87,18 +114,17 @@ static inline struct st7571_device *drm_to_st7571(struct drm_device *drm)

static int st7571_regmap_write(void *context, const void *data, size_t count)
{
	struct i2c_client *client = context;
	struct st7571_device *st7571 = i2c_get_clientdata(client);
	struct st7571_i2c_transport *t = context;
	int ret;

	struct i2c_msg msg = {
		.addr = st7571->client->addr,
		.flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0,
		.addr = t->client->addr,
		.flags = t->ignore_nak ? I2C_M_IGNORE_NAK : 0,
		.len = count,
		.buf = (u8 *)data
	};

	ret = i2c_transfer(st7571->client->adapter, &msg, 1);
	ret = i2c_transfer(t->client->adapter, &msg, 1);

	/*
	 * Unfortunately, there is no way to check if the transfer failed because of
@@ -106,7 +132,7 @@ static int st7571_regmap_write(void *context, const void *data, size_t count)
	 *
	 * However, if the transfer fails and ignore_nak is set, we know it is an error.
	 */
	if (ret < 0 && st7571->ignore_nak)
	if (ret < 0 && t->ignore_nak)
		return ret;

	return 0;
@@ -845,102 +871,135 @@ static int st7571_lcd_init(struct st7571_device *st7571)
	return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
}

static int st7571_probe(struct i2c_client *client)
static struct st7571_device *st7571_probe(struct device *dev,
					  struct regmap *regmap)
{
	struct st7571_device *st7571;
	struct drm_device *drm;
	int ret;

	st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
	st7571 = devm_drm_dev_alloc(dev, &st7571_driver,
				    struct st7571_device, drm);
	if (IS_ERR(st7571))
		return PTR_ERR(st7571);
		return st7571;

	drm = &st7571->drm;
	st7571->dev = &client->dev;
	st7571->client = client;
	i2c_set_clientdata(client, st7571);
	st7571->dev = dev;
	st7571->pdata = device_get_match_data(st7571->dev);

	ret = st7571->pdata->parse_dt(st7571);
	if (ret)
		return ret;
		return ERR_PTR(ret);

	ret = st7571_validate_parameters(st7571);
	if (ret)
		return ret;
		return ERR_PTR(ret);

	st7571->mode = st7571_mode(st7571);
	st7571->regmap = regmap;

	/*
	 * The hardware design could make it hard to detect a NAK on the I2C bus.
	 * If the adapter does not support protocol mangling do
	 * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
	 * cruft in the logs.
	 */
	if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
		st7571->ignore_nak = true;

	st7571->regmap = devm_regmap_init(st7571->dev, &st7571_regmap_bus,
					  client, &st7571_regmap_config);
	if (IS_ERR(st7571->regmap)) {
		return dev_err_probe(st7571->dev, PTR_ERR(st7571->regmap),
				     "Failed to initialize regmap\n");
	}

	st7571->hwbuf = devm_kzalloc(st7571->dev,
				     (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
				     GFP_KERNEL);
	if (!st7571->hwbuf)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	st7571->row = devm_kzalloc(st7571->dev,
				   (st7571->ncols * st7571->bpp),
				   GFP_KERNEL);
	if (!st7571->row)
		return -ENOMEM;
		return ERR_PTR(-ENOMEM);

	ret = st7571_mode_config_init(st7571);
	if (ret)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to initialize mode config\n");
	if (ret) {
		dev_err(st7571->dev, "Failed to initialize mode config\n");
		return ERR_PTR(ret);
	}

	ret = st7571_plane_init(st7571, st7571->pformat);
	if (ret)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to initialize primary plane\n");
	if (ret) {
		dev_err(st7571->dev, "Failed to initialize primary plane\n");
		return ERR_PTR(ret);
	}

	ret = st7571_crtc_init(st7571);
	if (ret < 0)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to initialize CRTC\n");
	if (ret < 0) {
		dev_err(st7571->dev, "Failed to initialize CRTC\n");
		return ERR_PTR(ret);
	}

	ret = st7571_encoder_init(st7571);
	if (ret < 0)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to initialize encoder\n");
	if (ret < 0) {
		dev_err(st7571->dev, "Failed to initialize encoder\n");
		return ERR_PTR(ret);
	}

	ret = st7571_connector_init(st7571);
	if (ret < 0)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to initialize connector\n");
	if (ret < 0) {
		dev_err(st7571->dev, "Failed to initialize connector\n");
		return ERR_PTR(ret);
	}

	drm_mode_config_reset(drm);

	ret = drm_dev_register(drm, 0);
	if (ret)
		return dev_err_probe(st7571->dev, ret,
				     "Failed to register DRM device\n");
	if (ret) {
		dev_err(st7571->dev, "Failed to register DRM device\n");
		return ERR_PTR(ret);
	}

	drm_client_setup(drm, NULL);
	return st7571;
}

static void st7571_remove(struct st7571_device *st7571)
{
	drm_dev_unplug(&st7571->drm);
}

static int st7571_i2c_probe(struct i2c_client *client)
{
	struct st7571_device *st7571;
	struct st7571_i2c_transport *t;
	struct regmap *regmap;

	t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
	if (!t)
		return -ENOMEM;

	t->client = client;

	/*
	 * The hardware design could make it hard to detect a NAK on the I2C bus.
	 * If the adapter does not support protocol mangling do
	 * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
	 * cruft in the logs.
	 */
	if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
		t->ignore_nak = true;

	regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
				  t, &st7571_regmap_config);
	if (IS_ERR(regmap)) {
		return dev_err_probe(&client->dev, PTR_ERR(regmap),
				     "Failed to initialize regmap\n");
	}

	st7571 = st7571_probe(&client->dev, regmap);
	if (IS_ERR(st7571))
		return dev_err_probe(&client->dev, PTR_ERR(st7571),
				     "Failed to initialize regmap\n");

	i2c_set_clientdata(client, st7571);
	return 0;
}

static void st7571_remove(struct i2c_client *client)
static void st7571_i2c_remove(struct i2c_client *client)
{
	struct st7571_device *st7571 = i2c_get_clientdata(client);

	drm_dev_unplug(&st7571->drm);
	st7571_remove(st7571);
}

static const struct st7571_panel_data st7567_config = {
@@ -986,8 +1045,8 @@ static struct i2c_driver st7571_i2c_driver = {
		.name = "st7571",
		.of_match_table = st7571_of_match,
	},
	.probe = st7571_probe,
	.remove = st7571_remove,
	.probe = st7571_i2c_probe,
	.remove = st7571_i2c_remove,
	.id_table = st7571_id,
};

+1 −24
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_format_helper.h>

#include <linux/regmap.h>

@@ -62,33 +63,9 @@ struct st7571_device {

	const struct st7571_panel_format *pformat;
	const struct st7571_panel_data *pdata;
	struct i2c_client *client;
	struct gpio_desc *reset;
	struct regmap *regmap;

	/*
	 * Depending on the hardware design, the acknowledge signal may be hard to
	 * recognize as a valid logic "0" level.
	 * Therefor, ignore NAK if possible to stay compatible with most hardware designs
	 * and off-the-shelf panels out there.
	 *
	 * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
	 *
	 * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
	 * I2C interface compatible.
	 * Separating acknowledge-output from serial data
	 * input is advantageous for chip-on-glass (COG) applications. In COG
	 * applications, the ITO resistance and the pull-up resistor will form a
	 * voltage  divider, which affects acknowledge-signal level. Larger ITO
	 * resistance will raise the acknowledged-signal level and system cannot
	 * recognize this level as a valid logic “0” level. By separating SDA_IN from
	 * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
	 * For applications which check acknowledge-bit, it is necessary to minimize
	 * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
	 *
	 */
	bool ignore_nak;

	bool grayscale;
	bool inverted;
	u32 height_mm;