diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2014-02-08 11:33:46 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-02-24 11:11:17 -0500 |
commit | b2b3593e331cce1718d7388f8a1182b5195be5fb (patch) | |
tree | 3f3edebc2f2425c9392b732e40891efd74e1e16e /drivers/media/i2c | |
parent | e68084c661953e7b7d78a952156d35e83187c038 (diff) |
[media] mt9t001: Add regulator support
The sensor needs two power supplies, VAA and VDD. Require a regulator
for each of them.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r-- | drivers/media/i2c/mt9t001.c | 206 |
1 files changed, 156 insertions, 50 deletions
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index d41c70eaf838..9a0bb063aa9b 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c | |||
@@ -13,8 +13,9 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/i2c.h> | 15 | #include <linux/i2c.h> |
16 | #include <linux/module.h> | ||
17 | #include <linux/log2.h> | 16 | #include <linux/log2.h> |
17 | #include <linux/module.h> | ||
18 | #include <linux/regulator/consumer.h> | ||
18 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
19 | #include <linux/videodev2.h> | 20 | #include <linux/videodev2.h> |
20 | #include <linux/v4l2-mediabus.h> | 21 | #include <linux/v4l2-mediabus.h> |
@@ -55,6 +56,7 @@ | |||
55 | #define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) | 56 | #define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) |
56 | #define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) | 57 | #define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) |
57 | #define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) | 58 | #define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) |
59 | #define MT9T001_OUTPUT_CONTROL_DEF 0x0002 | ||
58 | #define MT9T001_SHUTTER_WIDTH_HIGH 0x08 | 60 | #define MT9T001_SHUTTER_WIDTH_HIGH 0x08 |
59 | #define MT9T001_SHUTTER_WIDTH_LOW 0x09 | 61 | #define MT9T001_SHUTTER_WIDTH_LOW 0x09 |
60 | #define MT9T001_SHUTTER_WIDTH_MIN 1 | 62 | #define MT9T001_SHUTTER_WIDTH_MIN 1 |
@@ -116,6 +118,11 @@ struct mt9t001 { | |||
116 | struct v4l2_subdev subdev; | 118 | struct v4l2_subdev subdev; |
117 | struct media_pad pad; | 119 | struct media_pad pad; |
118 | 120 | ||
121 | struct regulator_bulk_data regulators[2]; | ||
122 | |||
123 | struct mutex power_lock; /* lock to protect power_count */ | ||
124 | int power_count; | ||
125 | |||
119 | struct v4l2_mbus_framefmt format; | 126 | struct v4l2_mbus_framefmt format; |
120 | struct v4l2_rect crop; | 127 | struct v4l2_rect crop; |
121 | 128 | ||
@@ -159,6 +166,62 @@ static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, | |||
159 | return 0; | 166 | return 0; |
160 | } | 167 | } |
161 | 168 | ||
169 | static int mt9t001_reset(struct mt9t001 *mt9t001) | ||
170 | { | ||
171 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
172 | int ret; | ||
173 | |||
174 | /* Reset the chip and stop data read out */ | ||
175 | ret = mt9t001_write(client, MT9T001_RESET, 1); | ||
176 | if (ret < 0) | ||
177 | return ret; | ||
178 | |||
179 | ret = mt9t001_write(client, MT9T001_RESET, 0); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | |||
183 | mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; | ||
184 | |||
185 | return mt9t001_set_output_control(mt9t001, | ||
186 | MT9T001_OUTPUT_CONTROL_CHIP_ENABLE, | ||
187 | 0); | ||
188 | } | ||
189 | |||
190 | static int mt9t001_power_on(struct mt9t001 *mt9t001) | ||
191 | { | ||
192 | /* Bring up the supplies */ | ||
193 | return regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators), | ||
194 | mt9t001->regulators); | ||
195 | } | ||
196 | |||
197 | static void mt9t001_power_off(struct mt9t001 *mt9t001) | ||
198 | { | ||
199 | regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), | ||
200 | mt9t001->regulators); | ||
201 | |||
202 | static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) | ||
203 | { | ||
204 | struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); | ||
205 | int ret; | ||
206 | |||
207 | if (!on) { | ||
208 | mt9t001_power_off(mt9t001); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | ret = mt9t001_power_on(mt9t001); | ||
213 | if (ret < 0) | ||
214 | return ret; | ||
215 | |||
216 | ret = mt9t001_reset(mt9t001); | ||
217 | if (ret < 0) { | ||
218 | dev_err(&client->dev, "Failed to reset the camera\n"); | ||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | return v4l2_ctrl_handler_setup(&mt9t001->ctrls); | ||
223 | } | ||
224 | |||
162 | /* ----------------------------------------------------------------------------- | 225 | /* ----------------------------------------------------------------------------- |
163 | * V4L2 subdev video operations | 226 | * V4L2 subdev video operations |
164 | */ | 227 | */ |
@@ -195,6 +258,7 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) | |||
195 | { | 258 | { |
196 | const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; | 259 | const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; |
197 | struct i2c_client *client = v4l2_get_subdevdata(subdev); | 260 | struct i2c_client *client = v4l2_get_subdevdata(subdev); |
261 | struct mt9t001_platform_data *pdata = client->dev.platform_data; | ||
198 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | 262 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); |
199 | struct v4l2_mbus_framefmt *format = &mt9t001->format; | 263 | struct v4l2_mbus_framefmt *format = &mt9t001->format; |
200 | struct v4l2_rect *crop = &mt9t001->crop; | 264 | struct v4l2_rect *crop = &mt9t001->crop; |
@@ -205,6 +269,14 @@ static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) | |||
205 | if (!enable) | 269 | if (!enable) |
206 | return mt9t001_set_output_control(mt9t001, mode, 0); | 270 | return mt9t001_set_output_control(mt9t001, mode, 0); |
207 | 271 | ||
272 | /* Configure the pixel clock polarity */ | ||
273 | if (pdata->clk_pol) { | ||
274 | ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, | ||
275 | MT9T001_PIXEL_CLOCK_INVERT); | ||
276 | if (ret < 0) | ||
277 | return ret; | ||
278 | } | ||
279 | |||
208 | /* Configure the window size and row/column bin */ | 280 | /* Configure the window size and row/column bin */ |
209 | hratio = DIV_ROUND_CLOSEST(crop->width, format->width); | 281 | hratio = DIV_ROUND_CLOSEST(crop->width, format->width); |
210 | vratio = DIV_ROUND_CLOSEST(crop->height, format->height); | 282 | vratio = DIV_ROUND_CLOSEST(crop->height, format->height); |
@@ -630,9 +702,67 @@ static const struct v4l2_ctrl_config mt9t001_gains[] = { | |||
630 | }; | 702 | }; |
631 | 703 | ||
632 | /* ----------------------------------------------------------------------------- | 704 | /* ----------------------------------------------------------------------------- |
705 | * V4L2 subdev core operations | ||
706 | */ | ||
707 | |||
708 | static int mt9t001_set_power(struct v4l2_subdev *subdev, int on) | ||
709 | { | ||
710 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
711 | int ret = 0; | ||
712 | |||
713 | mutex_lock(&mt9t001->power_lock); | ||
714 | |||
715 | /* If the power count is modified from 0 to != 0 or from != 0 to 0, | ||
716 | * update the power state. | ||
717 | */ | ||
718 | if (mt9t001->power_count == !on) { | ||
719 | ret = __mt9t001_set_power(mt9t001, !!on); | ||
720 | if (ret < 0) | ||
721 | goto out; | ||
722 | } | ||
723 | |||
724 | /* Update the power count. */ | ||
725 | mt9t001->power_count += on ? 1 : -1; | ||
726 | WARN_ON(mt9t001->power_count < 0); | ||
727 | |||
728 | out: | ||
729 | mutex_unlock(&mt9t001->power_lock); | ||
730 | return ret; | ||
731 | } | ||
732 | |||
733 | /* ----------------------------------------------------------------------------- | ||
633 | * V4L2 subdev internal operations | 734 | * V4L2 subdev internal operations |
634 | */ | 735 | */ |
635 | 736 | ||
737 | static int mt9t001_registered(struct v4l2_subdev *subdev) | ||
738 | { | ||
739 | struct i2c_client *client = v4l2_get_subdevdata(subdev); | ||
740 | struct mt9t001 *mt9t001 = to_mt9t001(subdev); | ||
741 | s32 data; | ||
742 | int ret; | ||
743 | |||
744 | ret = mt9t001_power_on(mt9t001); | ||
745 | if (ret < 0) { | ||
746 | dev_err(&client->dev, "MT9T001 power up failed\n"); | ||
747 | return ret; | ||
748 | } | ||
749 | |||
750 | /* Read out the chip version register */ | ||
751 | data = mt9t001_read(client, MT9T001_CHIP_VERSION); | ||
752 | mt9t001_power_off(mt9t001); | ||
753 | |||
754 | if (data != MT9T001_CHIP_ID) { | ||
755 | dev_err(&client->dev, | ||
756 | "MT9T001 not detected, wrong version 0x%04x\n", data); | ||
757 | return -ENODEV; | ||
758 | } | ||
759 | |||
760 | dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", | ||
761 | client->addr); | ||
762 | |||
763 | return 0; | ||
764 | } | ||
765 | |||
636 | static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | 766 | static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) |
637 | { | 767 | { |
638 | struct v4l2_mbus_framefmt *format; | 768 | struct v4l2_mbus_framefmt *format; |
@@ -651,9 +781,18 @@ static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | |||
651 | format->field = V4L2_FIELD_NONE; | 781 | format->field = V4L2_FIELD_NONE; |
652 | format->colorspace = V4L2_COLORSPACE_SRGB; | 782 | format->colorspace = V4L2_COLORSPACE_SRGB; |
653 | 783 | ||
654 | return 0; | 784 | return mt9t001_set_power(subdev, 1); |
655 | } | 785 | } |
656 | 786 | ||
787 | static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) | ||
788 | { | ||
789 | return mt9t001_set_power(subdev, 0); | ||
790 | } | ||
791 | |||
792 | static struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = { | ||
793 | .s_power = mt9t001_set_power, | ||
794 | }; | ||
795 | |||
657 | static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { | 796 | static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { |
658 | .s_stream = mt9t001_s_stream, | 797 | .s_stream = mt9t001_s_stream, |
659 | }; | 798 | }; |
@@ -668,58 +807,17 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { | |||
668 | }; | 807 | }; |
669 | 808 | ||
670 | static struct v4l2_subdev_ops mt9t001_subdev_ops = { | 809 | static struct v4l2_subdev_ops mt9t001_subdev_ops = { |
810 | .core = &mt9t001_subdev_core_ops, | ||
671 | .video = &mt9t001_subdev_video_ops, | 811 | .video = &mt9t001_subdev_video_ops, |
672 | .pad = &mt9t001_subdev_pad_ops, | 812 | .pad = &mt9t001_subdev_pad_ops, |
673 | }; | 813 | }; |
674 | 814 | ||
675 | static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { | 815 | static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { |
816 | .registered = mt9t001_registered, | ||
676 | .open = mt9t001_open, | 817 | .open = mt9t001_open, |
818 | .close = mt9t001_close, | ||
677 | }; | 819 | }; |
678 | 820 | ||
679 | static int mt9t001_video_probe(struct i2c_client *client) | ||
680 | { | ||
681 | struct mt9t001_platform_data *pdata = client->dev.platform_data; | ||
682 | s32 data; | ||
683 | int ret; | ||
684 | |||
685 | dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", | ||
686 | client->addr); | ||
687 | |||
688 | /* Reset the chip and stop data read out */ | ||
689 | ret = mt9t001_write(client, MT9T001_RESET, 1); | ||
690 | if (ret < 0) | ||
691 | return ret; | ||
692 | |||
693 | ret = mt9t001_write(client, MT9T001_RESET, 0); | ||
694 | if (ret < 0) | ||
695 | return ret; | ||
696 | |||
697 | ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); | ||
698 | if (ret < 0) | ||
699 | return ret; | ||
700 | |||
701 | /* Configure the pixel clock polarity */ | ||
702 | if (pdata->clk_pol) { | ||
703 | ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, | ||
704 | MT9T001_PIXEL_CLOCK_INVERT); | ||
705 | if (ret < 0) | ||
706 | return ret; | ||
707 | } | ||
708 | |||
709 | /* Read and check the sensor version */ | ||
710 | data = mt9t001_read(client, MT9T001_CHIP_VERSION); | ||
711 | if (data != MT9T001_CHIP_ID) { | ||
712 | dev_err(&client->dev, "MT9T001 not detected, wrong version " | ||
713 | "0x%04x\n", data); | ||
714 | return -ENODEV; | ||
715 | } | ||
716 | |||
717 | dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", | ||
718 | client->addr); | ||
719 | |||
720 | return ret; | ||
721 | } | ||
722 | |||
723 | static int mt9t001_probe(struct i2c_client *client, | 821 | static int mt9t001_probe(struct i2c_client *client, |
724 | const struct i2c_device_id *did) | 822 | const struct i2c_device_id *did) |
725 | { | 823 | { |
@@ -740,14 +838,22 @@ static int mt9t001_probe(struct i2c_client *client, | |||
740 | return -EIO; | 838 | return -EIO; |
741 | } | 839 | } |
742 | 840 | ||
743 | ret = mt9t001_video_probe(client); | ||
744 | if (ret < 0) | ||
745 | return ret; | ||
746 | |||
747 | mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL); | 841 | mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL); |
748 | if (!mt9t001) | 842 | if (!mt9t001) |
749 | return -ENOMEM; | 843 | return -ENOMEM; |
750 | 844 | ||
845 | mutex_init(&mt9t001->power_lock); | ||
846 | mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; | ||
847 | |||
848 | mt9t001->regulators[0].supply = "vdd"; | ||
849 | mt9t001->regulators[1].supply = "vaa"; | ||
850 | |||
851 | ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators); | ||
852 | if (ret < 0) { | ||
853 | dev_err(&client->dev, "Unable to get regulators\n"); | ||
854 | return ret; | ||
855 | } | ||
856 | |||
751 | v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + | 857 | v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + |
752 | ARRAY_SIZE(mt9t001_gains) + 4); | 858 | ARRAY_SIZE(mt9t001_gains) + 4); |
753 | 859 | ||