1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Sanyo LV5207LP LED Driver |
4 | * |
5 | * Copyright (C) 2013 Ideas on board SPRL |
6 | * |
7 | * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
8 | */ |
9 | |
10 | #include <linux/backlight.h> |
11 | #include <linux/err.h> |
12 | #include <linux/fb.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/module.h> |
15 | #include <linux/platform_data/lv5207lp.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #define LV5207LP_CTRL1 0x00 |
19 | #define LV5207LP_CPSW (1 << 7) |
20 | #define LV5207LP_SCTEN (1 << 6) |
21 | #define LV5207LP_C10 (1 << 5) |
22 | #define LV5207LP_CKSW (1 << 4) |
23 | #define LV5207LP_RSW (1 << 3) |
24 | #define LV5207LP_GSW (1 << 2) |
25 | #define LV5207LP_BSW (1 << 1) |
26 | #define LV5207LP_CTRL2 0x01 |
27 | #define LV5207LP_MSW (1 << 7) |
28 | #define LV5207LP_MLED4 (1 << 6) |
29 | #define LV5207LP_RED 0x02 |
30 | #define LV5207LP_GREEN 0x03 |
31 | #define LV5207LP_BLUE 0x04 |
32 | |
33 | #define LV5207LP_MAX_BRIGHTNESS 32 |
34 | |
35 | struct lv5207lp { |
36 | struct i2c_client *client; |
37 | struct backlight_device *backlight; |
38 | struct lv5207lp_platform_data *pdata; |
39 | }; |
40 | |
41 | static int lv5207lp_write(struct lv5207lp *lv, u8 reg, u8 data) |
42 | { |
43 | return i2c_smbus_write_byte_data(client: lv->client, command: reg, value: data); |
44 | } |
45 | |
46 | static int lv5207lp_backlight_update_status(struct backlight_device *backlight) |
47 | { |
48 | struct lv5207lp *lv = bl_get_data(bl_dev: backlight); |
49 | int brightness = backlight_get_brightness(bd: backlight); |
50 | |
51 | if (brightness) { |
52 | lv5207lp_write(lv, LV5207LP_CTRL1, |
53 | LV5207LP_CPSW | LV5207LP_C10 | LV5207LP_CKSW); |
54 | lv5207lp_write(lv, LV5207LP_CTRL2, |
55 | LV5207LP_MSW | LV5207LP_MLED4 | |
56 | (brightness - 1)); |
57 | } else { |
58 | lv5207lp_write(lv, LV5207LP_CTRL1, data: 0); |
59 | lv5207lp_write(lv, LV5207LP_CTRL2, data: 0); |
60 | } |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int lv5207lp_backlight_check_fb(struct backlight_device *backlight, |
66 | struct fb_info *info) |
67 | { |
68 | struct lv5207lp *lv = bl_get_data(bl_dev: backlight); |
69 | |
70 | return !lv->pdata->dev || lv->pdata->dev == info->device; |
71 | } |
72 | |
73 | static const struct backlight_ops lv5207lp_backlight_ops = { |
74 | .options = BL_CORE_SUSPENDRESUME, |
75 | .update_status = lv5207lp_backlight_update_status, |
76 | .check_fb = lv5207lp_backlight_check_fb, |
77 | }; |
78 | |
79 | static int lv5207lp_probe(struct i2c_client *client) |
80 | { |
81 | struct lv5207lp_platform_data *pdata = dev_get_platdata(dev: &client->dev); |
82 | struct backlight_device *backlight; |
83 | struct backlight_properties props; |
84 | struct lv5207lp *lv; |
85 | |
86 | if (pdata == NULL) { |
87 | dev_err(&client->dev, "No platform data supplied\n" ); |
88 | return -EINVAL; |
89 | } |
90 | |
91 | if (!i2c_check_functionality(adap: client->adapter, |
92 | I2C_FUNC_SMBUS_BYTE_DATA)) { |
93 | dev_warn(&client->dev, |
94 | "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n" ); |
95 | return -EIO; |
96 | } |
97 | |
98 | lv = devm_kzalloc(dev: &client->dev, size: sizeof(*lv), GFP_KERNEL); |
99 | if (!lv) |
100 | return -ENOMEM; |
101 | |
102 | lv->client = client; |
103 | lv->pdata = pdata; |
104 | |
105 | memset(&props, 0, sizeof(props)); |
106 | props.type = BACKLIGHT_RAW; |
107 | props.max_brightness = min_t(unsigned int, pdata->max_value, |
108 | LV5207LP_MAX_BRIGHTNESS); |
109 | props.brightness = clamp_t(unsigned int, pdata->def_value, 0, |
110 | props.max_brightness); |
111 | |
112 | backlight = devm_backlight_device_register(dev: &client->dev, |
113 | name: dev_name(dev: &client->dev), parent: &lv->client->dev, |
114 | devdata: lv, ops: &lv5207lp_backlight_ops, props: &props); |
115 | if (IS_ERR(ptr: backlight)) { |
116 | dev_err(&client->dev, "failed to register backlight\n" ); |
117 | return PTR_ERR(ptr: backlight); |
118 | } |
119 | |
120 | backlight_update_status(bd: backlight); |
121 | i2c_set_clientdata(client, data: backlight); |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static void lv5207lp_remove(struct i2c_client *client) |
127 | { |
128 | struct backlight_device *backlight = i2c_get_clientdata(client); |
129 | |
130 | backlight->props.brightness = 0; |
131 | backlight_update_status(bd: backlight); |
132 | } |
133 | |
134 | static const struct i2c_device_id lv5207lp_ids[] = { |
135 | { "lv5207lp" , 0 }, |
136 | { } |
137 | }; |
138 | MODULE_DEVICE_TABLE(i2c, lv5207lp_ids); |
139 | |
140 | static struct i2c_driver lv5207lp_driver = { |
141 | .driver = { |
142 | .name = "lv5207lp" , |
143 | }, |
144 | .probe = lv5207lp_probe, |
145 | .remove = lv5207lp_remove, |
146 | .id_table = lv5207lp_ids, |
147 | }; |
148 | |
149 | module_i2c_driver(lv5207lp_driver); |
150 | |
151 | MODULE_DESCRIPTION("Sanyo LV5207LP Backlight Driver" ); |
152 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>" ); |
153 | MODULE_LICENSE("GPL" ); |
154 | |