diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2015-06-08 05:02:20 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2015-06-22 16:55:17 -0400 |
commit | 0b380186a5f12f12e4c780dd5413731b4ec48670 (patch) | |
tree | a99cc3a71f4cf01456db9d0096c3b17828cd3f9c | |
parent | 42bd6f59ae90244484746696aabcafc0003f59c7 (diff) |
leds: max77693: add support for V4L2 Flash sub-device
Add support for V4L2 Flash sub-device to the max77693 LED Flash class
driver. The support allows for V4L2 Flash sub-device to take the control
of the LED Flash class device.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r-- | drivers/leds/leds-max77693.c | 129 |
1 files changed, 123 insertions, 6 deletions
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c index eecaa9292b2f..b8b0eec7b540 100644 --- a/drivers/leds/leds-max77693.c +++ b/drivers/leds/leds-max77693.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/regmap.h> | 20 | #include <linux/regmap.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/workqueue.h> | 22 | #include <linux/workqueue.h> |
23 | #include <media/v4l2-flash-led-class.h> | ||
23 | 24 | ||
24 | #define MODE_OFF 0 | 25 | #define MODE_OFF 0 |
25 | #define MODE_FLASH(a) (1 << (a)) | 26 | #define MODE_FLASH(a) (1 << (a)) |
@@ -62,6 +63,8 @@ struct max77693_sub_led { | |||
62 | struct led_classdev_flash fled_cdev; | 63 | struct led_classdev_flash fled_cdev; |
63 | /* assures led-triggers compatibility */ | 64 | /* assures led-triggers compatibility */ |
64 | struct work_struct work_brightness_set; | 65 | struct work_struct work_brightness_set; |
66 | /* V4L2 Flash device */ | ||
67 | struct v4l2_flash *v4l2_flash; | ||
65 | 68 | ||
66 | /* brightness cache */ | 69 | /* brightness cache */ |
67 | unsigned int torch_brightness; | 70 | unsigned int torch_brightness; |
@@ -627,7 +630,8 @@ static int max77693_led_flash_timeout_set( | |||
627 | } | 630 | } |
628 | 631 | ||
629 | static int max77693_led_parse_dt(struct max77693_led_device *led, | 632 | static int max77693_led_parse_dt(struct max77693_led_device *led, |
630 | struct max77693_led_config_data *cfg) | 633 | struct max77693_led_config_data *cfg, |
634 | struct device_node **sub_nodes) | ||
631 | { | 635 | { |
632 | struct device *dev = &led->pdev->dev; | 636 | struct device *dev = &led->pdev->dev; |
633 | struct max77693_sub_led *sub_leds = led->sub_leds; | 637 | struct max77693_sub_led *sub_leds = led->sub_leds; |
@@ -674,6 +678,13 @@ static int max77693_led_parse_dt(struct max77693_led_device *led, | |||
674 | return -EINVAL; | 678 | return -EINVAL; |
675 | } | 679 | } |
676 | 680 | ||
681 | if (sub_nodes[fled_id]) { | ||
682 | dev_err(dev, | ||
683 | "Conflicting \"led-sources\" DT properties\n"); | ||
684 | return -EINVAL; | ||
685 | } | ||
686 | |||
687 | sub_nodes[fled_id] = child_node; | ||
677 | sub_leds[fled_id].fled_id = fled_id; | 688 | sub_leds[fled_id].fled_id = fled_id; |
678 | 689 | ||
679 | cfg->label[fled_id] = | 690 | cfg->label[fled_id] = |
@@ -786,11 +797,12 @@ static void max77693_led_validate_configuration(struct max77693_led_device *led, | |||
786 | } | 797 | } |
787 | 798 | ||
788 | static int max77693_led_get_configuration(struct max77693_led_device *led, | 799 | static int max77693_led_get_configuration(struct max77693_led_device *led, |
789 | struct max77693_led_config_data *cfg) | 800 | struct max77693_led_config_data *cfg, |
801 | struct device_node **sub_nodes) | ||
790 | { | 802 | { |
791 | int ret; | 803 | int ret; |
792 | 804 | ||
793 | ret = max77693_led_parse_dt(led, cfg); | 805 | ret = max77693_led_parse_dt(led, cfg, sub_nodes); |
794 | if (ret < 0) | 806 | if (ret < 0) |
795 | return ret; | 807 | return ret; |
796 | 808 | ||
@@ -838,6 +850,71 @@ static void max77693_init_flash_settings(struct max77693_sub_led *sub_led, | |||
838 | setting->val = setting->max; | 850 | setting->val = setting->max; |
839 | } | 851 | } |
840 | 852 | ||
853 | #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS) | ||
854 | |||
855 | static int max77693_led_external_strobe_set( | ||
856 | struct v4l2_flash *v4l2_flash, | ||
857 | bool enable) | ||
858 | { | ||
859 | struct max77693_sub_led *sub_led = | ||
860 | flcdev_to_sub_led(v4l2_flash->fled_cdev); | ||
861 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
862 | int fled_id = sub_led->fled_id; | ||
863 | int ret; | ||
864 | |||
865 | mutex_lock(&led->lock); | ||
866 | |||
867 | if (enable) | ||
868 | ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id)); | ||
869 | else | ||
870 | ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id)); | ||
871 | |||
872 | mutex_unlock(&led->lock); | ||
873 | |||
874 | return ret; | ||
875 | } | ||
876 | |||
877 | static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led, | ||
878 | struct max77693_led_config_data *led_cfg, | ||
879 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
880 | { | ||
881 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
882 | struct device *dev = &led->pdev->dev; | ||
883 | struct max77693_dev *iodev = dev_get_drvdata(dev->parent); | ||
884 | struct i2c_client *i2c = iodev->i2c; | ||
885 | struct led_flash_setting *s; | ||
886 | |||
887 | snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name), | ||
888 | "%s %d-%04x", sub_led->fled_cdev.led_cdev.name, | ||
889 | i2c_adapter_id(i2c->adapter), i2c->addr); | ||
890 | |||
891 | s = &v4l2_sd_cfg->torch_intensity; | ||
892 | s->min = TORCH_IOUT_MIN; | ||
893 | s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP; | ||
894 | s->step = TORCH_IOUT_STEP; | ||
895 | s->val = s->max; | ||
896 | |||
897 | /* Init flash faults config */ | ||
898 | v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | | ||
899 | LED_FAULT_SHORT_CIRCUIT | | ||
900 | LED_FAULT_OVER_CURRENT; | ||
901 | |||
902 | v4l2_sd_cfg->has_external_strobe = true; | ||
903 | } | ||
904 | |||
905 | static const struct v4l2_flash_ops v4l2_flash_ops = { | ||
906 | .external_strobe_set = max77693_led_external_strobe_set, | ||
907 | }; | ||
908 | #else | ||
909 | static inline void max77693_init_v4l2_flash_config( | ||
910 | struct max77693_sub_led *sub_led, | ||
911 | struct max77693_led_config_data *led_cfg, | ||
912 | struct v4l2_flash_config *v4l2_sd_cfg) | ||
913 | { | ||
914 | } | ||
915 | static const struct v4l2_flash_ops v4l2_flash_ops; | ||
916 | #endif | ||
917 | |||
841 | static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, | 918 | static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, |
842 | struct max77693_led_config_data *led_cfg) | 919 | struct max77693_led_config_data *led_cfg) |
843 | { | 920 | { |
@@ -870,12 +947,45 @@ static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led, | |||
870 | sub_led->flash_timeout = fled_cdev->timeout.val; | 947 | sub_led->flash_timeout = fled_cdev->timeout.val; |
871 | } | 948 | } |
872 | 949 | ||
950 | static int max77693_register_led(struct max77693_sub_led *sub_led, | ||
951 | struct max77693_led_config_data *led_cfg, | ||
952 | struct device_node *sub_node) | ||
953 | { | ||
954 | struct max77693_led_device *led = sub_led_to_led(sub_led); | ||
955 | struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev; | ||
956 | struct device *dev = &led->pdev->dev; | ||
957 | struct v4l2_flash_config v4l2_sd_cfg = {}; | ||
958 | int ret; | ||
959 | |||
960 | /* Register in the LED subsystem */ | ||
961 | ret = led_classdev_flash_register(dev, fled_cdev); | ||
962 | if (ret < 0) | ||
963 | return ret; | ||
964 | |||
965 | max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg); | ||
966 | |||
967 | /* Register in the V4L2 subsystem. */ | ||
968 | sub_led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL, | ||
969 | &v4l2_flash_ops, &v4l2_sd_cfg); | ||
970 | if (IS_ERR(sub_led->v4l2_flash)) { | ||
971 | ret = PTR_ERR(sub_led->v4l2_flash); | ||
972 | goto err_v4l2_flash_init; | ||
973 | } | ||
974 | |||
975 | return 0; | ||
976 | |||
977 | err_v4l2_flash_init: | ||
978 | led_classdev_flash_unregister(fled_cdev); | ||
979 | return ret; | ||
980 | } | ||
981 | |||
873 | static int max77693_led_probe(struct platform_device *pdev) | 982 | static int max77693_led_probe(struct platform_device *pdev) |
874 | { | 983 | { |
875 | struct device *dev = &pdev->dev; | 984 | struct device *dev = &pdev->dev; |
876 | struct max77693_dev *iodev = dev_get_drvdata(dev->parent); | 985 | struct max77693_dev *iodev = dev_get_drvdata(dev->parent); |
877 | struct max77693_led_device *led; | 986 | struct max77693_led_device *led; |
878 | struct max77693_sub_led *sub_leds; | 987 | struct max77693_sub_led *sub_leds; |
988 | struct device_node *sub_nodes[2] = {}; | ||
879 | struct max77693_led_config_data led_cfg = {}; | 989 | struct max77693_led_config_data led_cfg = {}; |
880 | int init_fled_cdev[2], i, ret; | 990 | int init_fled_cdev[2], i, ret; |
881 | 991 | ||
@@ -889,7 +999,7 @@ static int max77693_led_probe(struct platform_device *pdev) | |||
889 | sub_leds = led->sub_leds; | 999 | sub_leds = led->sub_leds; |
890 | 1000 | ||
891 | platform_set_drvdata(pdev, led); | 1001 | platform_set_drvdata(pdev, led); |
892 | ret = max77693_led_get_configuration(led, &led_cfg); | 1002 | ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes); |
893 | if (ret < 0) | 1003 | if (ret < 0) |
894 | return ret; | 1004 | return ret; |
895 | 1005 | ||
@@ -911,8 +1021,12 @@ static int max77693_led_probe(struct platform_device *pdev) | |||
911 | /* Initialize LED Flash class device */ | 1021 | /* Initialize LED Flash class device */ |
912 | max77693_init_fled_cdev(&sub_leds[i], &led_cfg); | 1022 | max77693_init_fled_cdev(&sub_leds[i], &led_cfg); |
913 | 1023 | ||
914 | /* Register LED Flash class device */ | 1024 | /* |
915 | ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev); | 1025 | * Register LED Flash class device and corresponding |
1026 | * V4L2 Flash device. | ||
1027 | */ | ||
1028 | ret = max77693_register_led(&sub_leds[i], &led_cfg, | ||
1029 | sub_nodes[i]); | ||
916 | if (ret < 0) { | 1030 | if (ret < 0) { |
917 | /* | 1031 | /* |
918 | * At this moment FLED1 might have been already | 1032 | * At this moment FLED1 might have been already |
@@ -931,6 +1045,7 @@ err_register_led2: | |||
931 | /* It is possible than only FLED2 was to be registered */ | 1045 | /* It is possible than only FLED2 was to be registered */ |
932 | if (!init_fled_cdev[FLED1]) | 1046 | if (!init_fled_cdev[FLED1]) |
933 | goto err_register_led1; | 1047 | goto err_register_led1; |
1048 | v4l2_flash_release(sub_leds[FLED1].v4l2_flash); | ||
934 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); | 1049 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); |
935 | err_register_led1: | 1050 | err_register_led1: |
936 | mutex_destroy(&led->lock); | 1051 | mutex_destroy(&led->lock); |
@@ -944,11 +1059,13 @@ static int max77693_led_remove(struct platform_device *pdev) | |||
944 | struct max77693_sub_led *sub_leds = led->sub_leds; | 1059 | struct max77693_sub_led *sub_leds = led->sub_leds; |
945 | 1060 | ||
946 | if (led->iout_joint || max77693_fled_used(led, FLED1)) { | 1061 | if (led->iout_joint || max77693_fled_used(led, FLED1)) { |
1062 | v4l2_flash_release(sub_leds[FLED1].v4l2_flash); | ||
947 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); | 1063 | led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev); |
948 | cancel_work_sync(&sub_leds[FLED1].work_brightness_set); | 1064 | cancel_work_sync(&sub_leds[FLED1].work_brightness_set); |
949 | } | 1065 | } |
950 | 1066 | ||
951 | if (!led->iout_joint && max77693_fled_used(led, FLED2)) { | 1067 | if (!led->iout_joint && max77693_fled_used(led, FLED2)) { |
1068 | v4l2_flash_release(sub_leds[FLED2].v4l2_flash); | ||
952 | led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); | 1069 | led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev); |
953 | cancel_work_sync(&sub_leds[FLED2].work_brightness_set); | 1070 | cancel_work_sync(&sub_leds[FLED2].work_brightness_set); |
954 | } | 1071 | } |