18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Sanyo LV5207LP LED Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Ideas on board SPRL
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/backlight.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/fb.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_data/lv5207lp.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define LV5207LP_CTRL1			0x00
198c2ecf20Sopenharmony_ci#define LV5207LP_CPSW			(1 << 7)
208c2ecf20Sopenharmony_ci#define LV5207LP_SCTEN			(1 << 6)
218c2ecf20Sopenharmony_ci#define LV5207LP_C10			(1 << 5)
228c2ecf20Sopenharmony_ci#define LV5207LP_CKSW			(1 << 4)
238c2ecf20Sopenharmony_ci#define LV5207LP_RSW			(1 << 3)
248c2ecf20Sopenharmony_ci#define LV5207LP_GSW			(1 << 2)
258c2ecf20Sopenharmony_ci#define LV5207LP_BSW			(1 << 1)
268c2ecf20Sopenharmony_ci#define LV5207LP_CTRL2			0x01
278c2ecf20Sopenharmony_ci#define LV5207LP_MSW			(1 << 7)
288c2ecf20Sopenharmony_ci#define LV5207LP_MLED4			(1 << 6)
298c2ecf20Sopenharmony_ci#define LV5207LP_RED			0x02
308c2ecf20Sopenharmony_ci#define LV5207LP_GREEN			0x03
318c2ecf20Sopenharmony_ci#define LV5207LP_BLUE			0x04
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define LV5207LP_MAX_BRIGHTNESS		32
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct lv5207lp {
368c2ecf20Sopenharmony_ci	struct i2c_client *client;
378c2ecf20Sopenharmony_ci	struct backlight_device *backlight;
388c2ecf20Sopenharmony_ci	struct lv5207lp_platform_data *pdata;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int lv5207lp_write(struct lv5207lp *lv, u8 reg, u8 data)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(lv->client, reg, data);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int lv5207lp_backlight_update_status(struct backlight_device *backlight)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct lv5207lp *lv = bl_get_data(backlight);
498c2ecf20Sopenharmony_ci	int brightness = backlight_get_brightness(backlight);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (brightness) {
528c2ecf20Sopenharmony_ci		lv5207lp_write(lv, LV5207LP_CTRL1,
538c2ecf20Sopenharmony_ci			       LV5207LP_CPSW | LV5207LP_C10 | LV5207LP_CKSW);
548c2ecf20Sopenharmony_ci		lv5207lp_write(lv, LV5207LP_CTRL2,
558c2ecf20Sopenharmony_ci			       LV5207LP_MSW | LV5207LP_MLED4 |
568c2ecf20Sopenharmony_ci			       (brightness - 1));
578c2ecf20Sopenharmony_ci	} else {
588c2ecf20Sopenharmony_ci		lv5207lp_write(lv, LV5207LP_CTRL1, 0);
598c2ecf20Sopenharmony_ci		lv5207lp_write(lv, LV5207LP_CTRL2, 0);
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int lv5207lp_backlight_check_fb(struct backlight_device *backlight,
668c2ecf20Sopenharmony_ci				       struct fb_info *info)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct lv5207lp *lv = bl_get_data(backlight);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return lv->pdata->fbdev == NULL || lv->pdata->fbdev == info->device;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct backlight_ops lv5207lp_backlight_ops = {
748c2ecf20Sopenharmony_ci	.options	= BL_CORE_SUSPENDRESUME,
758c2ecf20Sopenharmony_ci	.update_status	= lv5207lp_backlight_update_status,
768c2ecf20Sopenharmony_ci	.check_fb	= lv5207lp_backlight_check_fb,
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int lv5207lp_probe(struct i2c_client *client,
808c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct lv5207lp_platform_data *pdata = dev_get_platdata(&client->dev);
838c2ecf20Sopenharmony_ci	struct backlight_device *backlight;
848c2ecf20Sopenharmony_ci	struct backlight_properties props;
858c2ecf20Sopenharmony_ci	struct lv5207lp *lv;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (pdata == NULL) {
888c2ecf20Sopenharmony_ci		dev_err(&client->dev, "No platform data supplied\n");
898c2ecf20Sopenharmony_ci		return -EINVAL;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter,
938c2ecf20Sopenharmony_ci				     I2C_FUNC_SMBUS_BYTE_DATA)) {
948c2ecf20Sopenharmony_ci		dev_warn(&client->dev,
958c2ecf20Sopenharmony_ci			 "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
968c2ecf20Sopenharmony_ci		return -EIO;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	lv = devm_kzalloc(&client->dev, sizeof(*lv), GFP_KERNEL);
1008c2ecf20Sopenharmony_ci	if (!lv)
1018c2ecf20Sopenharmony_ci		return -ENOMEM;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	lv->client = client;
1048c2ecf20Sopenharmony_ci	lv->pdata = pdata;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(props));
1078c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_RAW;
1088c2ecf20Sopenharmony_ci	props.max_brightness = min_t(unsigned int, pdata->max_value,
1098c2ecf20Sopenharmony_ci				     LV5207LP_MAX_BRIGHTNESS);
1108c2ecf20Sopenharmony_ci	props.brightness = clamp_t(unsigned int, pdata->def_value, 0,
1118c2ecf20Sopenharmony_ci				   props.max_brightness);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	backlight = devm_backlight_device_register(&client->dev,
1148c2ecf20Sopenharmony_ci				dev_name(&client->dev), &lv->client->dev,
1158c2ecf20Sopenharmony_ci				lv, &lv5207lp_backlight_ops, &props);
1168c2ecf20Sopenharmony_ci	if (IS_ERR(backlight)) {
1178c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to register backlight\n");
1188c2ecf20Sopenharmony_ci		return PTR_ERR(backlight);
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	backlight_update_status(backlight);
1228c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, backlight);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int lv5207lp_remove(struct i2c_client *client)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct backlight_device *backlight = i2c_get_clientdata(client);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	backlight->props.brightness = 0;
1328c2ecf20Sopenharmony_ci	backlight_update_status(backlight);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic const struct i2c_device_id lv5207lp_ids[] = {
1388c2ecf20Sopenharmony_ci	{ "lv5207lp", 0 },
1398c2ecf20Sopenharmony_ci	{ }
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, lv5207lp_ids);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct i2c_driver lv5207lp_driver = {
1448c2ecf20Sopenharmony_ci	.driver = {
1458c2ecf20Sopenharmony_ci		.name = "lv5207lp",
1468c2ecf20Sopenharmony_ci	},
1478c2ecf20Sopenharmony_ci	.probe = lv5207lp_probe,
1488c2ecf20Sopenharmony_ci	.remove = lv5207lp_remove,
1498c2ecf20Sopenharmony_ci	.id_table = lv5207lp_ids,
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cimodule_i2c_driver(lv5207lp_driver);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sanyo LV5207LP Backlight Driver");
1558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
1568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
157