diff options
Diffstat (limited to 'drivers/media/video/ov2640.c')
-rw-r--r-- | drivers/media/video/ov2640.c | 178 |
1 files changed, 52 insertions, 126 deletions
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index 9ce2fa037b9..b5247cb64fd 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c | |||
@@ -18,11 +18,13 @@ | |||
18 | #include <linux/i2c.h> | 18 | #include <linux/i2c.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/v4l2-mediabus.h> | ||
21 | #include <linux/videodev2.h> | 22 | #include <linux/videodev2.h> |
23 | |||
24 | #include <media/soc_camera.h> | ||
22 | #include <media/v4l2-chip-ident.h> | 25 | #include <media/v4l2-chip-ident.h> |
23 | #include <media/v4l2-subdev.h> | 26 | #include <media/v4l2-subdev.h> |
24 | #include <media/soc_camera.h> | 27 | #include <media/v4l2-ctrls.h> |
25 | #include <media/soc_mediabus.h> | ||
26 | 28 | ||
27 | #define VAL_SET(x, mask, rshift, lshift) \ | 29 | #define VAL_SET(x, mask, rshift, lshift) \ |
28 | ((((x) >> rshift) & mask) << lshift) | 30 | ((((x) >> rshift) & mask) << lshift) |
@@ -299,12 +301,10 @@ struct ov2640_win_size { | |||
299 | 301 | ||
300 | struct ov2640_priv { | 302 | struct ov2640_priv { |
301 | struct v4l2_subdev subdev; | 303 | struct v4l2_subdev subdev; |
302 | struct ov2640_camera_info *info; | 304 | struct v4l2_ctrl_handler hdl; |
303 | enum v4l2_mbus_pixelcode cfmt_code; | 305 | enum v4l2_mbus_pixelcode cfmt_code; |
304 | const struct ov2640_win_size *win; | 306 | const struct ov2640_win_size *win; |
305 | int model; | 307 | int model; |
306 | u16 flag_vflip:1; | ||
307 | u16 flag_hflip:1; | ||
308 | }; | 308 | }; |
309 | 309 | ||
310 | /* | 310 | /* |
@@ -610,29 +610,6 @@ static enum v4l2_mbus_pixelcode ov2640_codes[] = { | |||
610 | }; | 610 | }; |
611 | 611 | ||
612 | /* | 612 | /* |
613 | * Supported controls | ||
614 | */ | ||
615 | static const struct v4l2_queryctrl ov2640_controls[] = { | ||
616 | { | ||
617 | .id = V4L2_CID_VFLIP, | ||
618 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
619 | .name = "Flip Vertically", | ||
620 | .minimum = 0, | ||
621 | .maximum = 1, | ||
622 | .step = 1, | ||
623 | .default_value = 0, | ||
624 | }, { | ||
625 | .id = V4L2_CID_HFLIP, | ||
626 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
627 | .name = "Flip Horizontally", | ||
628 | .minimum = 0, | ||
629 | .maximum = 1, | ||
630 | .step = 1, | ||
631 | .default_value = 0, | ||
632 | }, | ||
633 | }; | ||
634 | |||
635 | /* | ||
636 | * General functions | 613 | * General functions |
637 | */ | 614 | */ |
638 | static struct ov2640_priv *to_ov2640(const struct i2c_client *client) | 615 | static struct ov2640_priv *to_ov2640(const struct i2c_client *client) |
@@ -701,81 +678,23 @@ static int ov2640_s_stream(struct v4l2_subdev *sd, int enable) | |||
701 | return 0; | 678 | return 0; |
702 | } | 679 | } |
703 | 680 | ||
704 | static int ov2640_set_bus_param(struct soc_camera_device *icd, | 681 | static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) |
705 | unsigned long flags) | ||
706 | { | ||
707 | struct soc_camera_link *icl = to_soc_camera_link(icd); | ||
708 | unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; | ||
709 | |||
710 | /* Only one width bit may be set */ | ||
711 | if (!is_power_of_2(width_flag)) | ||
712 | return -EINVAL; | ||
713 | |||
714 | if (icl->set_bus_param) | ||
715 | return icl->set_bus_param(icl, width_flag); | ||
716 | |||
717 | /* | ||
718 | * Without board specific bus width settings we support only the | ||
719 | * sensors native bus width witch are tested working | ||
720 | */ | ||
721 | if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8)) | ||
722 | return 0; | ||
723 | |||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd) | ||
728 | { | ||
729 | struct soc_camera_link *icl = to_soc_camera_link(icd); | ||
730 | unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | | ||
731 | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | | ||
732 | SOCAM_DATA_ACTIVE_HIGH; | ||
733 | |||
734 | if (icl->query_bus_param) | ||
735 | flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; | ||
736 | else | ||
737 | flags |= SOCAM_DATAWIDTH_10; | ||
738 | |||
739 | return soc_camera_apply_sensor_flags(icl, flags); | ||
740 | } | ||
741 | |||
742 | static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
743 | { | 682 | { |
683 | struct v4l2_subdev *sd = | ||
684 | &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev; | ||
744 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 685 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
745 | struct ov2640_priv *priv = to_ov2640(client); | ||
746 | |||
747 | switch (ctrl->id) { | ||
748 | case V4L2_CID_VFLIP: | ||
749 | ctrl->value = priv->flag_vflip; | ||
750 | break; | ||
751 | case V4L2_CID_HFLIP: | ||
752 | ctrl->value = priv->flag_hflip; | ||
753 | break; | ||
754 | } | ||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
759 | { | ||
760 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
761 | struct ov2640_priv *priv = to_ov2640(client); | ||
762 | int ret = 0; | ||
763 | u8 val; | 686 | u8 val; |
764 | 687 | ||
765 | switch (ctrl->id) { | 688 | switch (ctrl->id) { |
766 | case V4L2_CID_VFLIP: | 689 | case V4L2_CID_VFLIP: |
767 | val = ctrl->value ? REG04_VFLIP_IMG : 0x00; | 690 | val = ctrl->val ? REG04_VFLIP_IMG : 0x00; |
768 | priv->flag_vflip = ctrl->value ? 1 : 0; | 691 | return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); |
769 | ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); | ||
770 | break; | ||
771 | case V4L2_CID_HFLIP: | 692 | case V4L2_CID_HFLIP: |
772 | val = ctrl->value ? REG04_HFLIP_IMG : 0x00; | 693 | val = ctrl->val ? REG04_HFLIP_IMG : 0x00; |
773 | priv->flag_hflip = ctrl->value ? 1 : 0; | 694 | return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); |
774 | ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); | ||
775 | break; | ||
776 | } | 695 | } |
777 | 696 | ||
778 | return ret; | 697 | return -EINVAL; |
779 | } | 698 | } |
780 | 699 | ||
781 | static int ov2640_g_chip_ident(struct v4l2_subdev *sd, | 700 | static int ov2640_g_chip_ident(struct v4l2_subdev *sd, |
@@ -1023,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) | |||
1023 | return 0; | 942 | return 0; |
1024 | } | 943 | } |
1025 | 944 | ||
1026 | static int ov2640_video_probe(struct soc_camera_device *icd, | 945 | static int ov2640_video_probe(struct i2c_client *client) |
1027 | struct i2c_client *client) | ||
1028 | { | 946 | { |
1029 | struct ov2640_priv *priv = to_ov2640(client); | 947 | struct ov2640_priv *priv = to_ov2640(client); |
1030 | u8 pid, ver, midh, midl; | 948 | u8 pid, ver, midh, midl; |
1031 | const char *devname; | 949 | const char *devname; |
1032 | int ret; | 950 | int ret; |
1033 | 951 | ||
1034 | /* We must have a parent by now. And it cannot be a wrong one. */ | ||
1035 | BUG_ON(!icd->parent || | ||
1036 | to_soc_camera_host(icd->parent)->nr != icd->iface); | ||
1037 | |||
1038 | /* | 952 | /* |
1039 | * check and show product ID and manufacturer ID | 953 | * check and show product ID and manufacturer ID |
1040 | */ | 954 | */ |
@@ -1060,22 +974,17 @@ static int ov2640_video_probe(struct soc_camera_device *icd, | |||
1060 | "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", | 974 | "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", |
1061 | devname, pid, ver, midh, midl); | 975 | devname, pid, ver, midh, midl); |
1062 | 976 | ||
1063 | return 0; | 977 | return v4l2_ctrl_handler_setup(&priv->hdl); |
1064 | 978 | ||
1065 | err: | 979 | err: |
1066 | return ret; | 980 | return ret; |
1067 | } | 981 | } |
1068 | 982 | ||
1069 | static struct soc_camera_ops ov2640_ops = { | 983 | static const struct v4l2_ctrl_ops ov2640_ctrl_ops = { |
1070 | .set_bus_param = ov2640_set_bus_param, | 984 | .s_ctrl = ov2640_s_ctrl, |
1071 | .query_bus_param = ov2640_query_bus_param, | ||
1072 | .controls = ov2640_controls, | ||
1073 | .num_controls = ARRAY_SIZE(ov2640_controls), | ||
1074 | }; | 985 | }; |
1075 | 986 | ||
1076 | static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { | 987 | static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { |
1077 | .g_ctrl = ov2640_g_ctrl, | ||
1078 | .s_ctrl = ov2640_s_ctrl, | ||
1079 | .g_chip_ident = ov2640_g_chip_ident, | 988 | .g_chip_ident = ov2640_g_chip_ident, |
1080 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 989 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
1081 | .g_register = ov2640_g_register, | 990 | .g_register = ov2640_g_register, |
@@ -1083,6 +992,21 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { | |||
1083 | #endif | 992 | #endif |
1084 | }; | 993 | }; |
1085 | 994 | ||
995 | static int ov2640_g_mbus_config(struct v4l2_subdev *sd, | ||
996 | struct v4l2_mbus_config *cfg) | ||
997 | { | ||
998 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
999 | struct soc_camera_link *icl = soc_camera_i2c_to_link(client); | ||
1000 | |||
1001 | cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | | ||
1002 | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | | ||
1003 | V4L2_MBUS_DATA_ACTIVE_HIGH; | ||
1004 | cfg->type = V4L2_MBUS_PARALLEL; | ||
1005 | cfg->flags = soc_camera_apply_board_flags(icl, cfg); | ||
1006 | |||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1086 | static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { | 1010 | static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { |
1087 | .s_stream = ov2640_s_stream, | 1011 | .s_stream = ov2640_s_stream, |
1088 | .g_mbus_fmt = ov2640_g_fmt, | 1012 | .g_mbus_fmt = ov2640_g_fmt, |
@@ -1091,6 +1015,7 @@ static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { | |||
1091 | .cropcap = ov2640_cropcap, | 1015 | .cropcap = ov2640_cropcap, |
1092 | .g_crop = ov2640_g_crop, | 1016 | .g_crop = ov2640_g_crop, |
1093 | .enum_mbus_fmt = ov2640_enum_fmt, | 1017 | .enum_mbus_fmt = ov2640_enum_fmt, |
1018 | .g_mbus_config = ov2640_g_mbus_config, | ||
1094 | }; | 1019 | }; |
1095 | 1020 | ||
1096 | static struct v4l2_subdev_ops ov2640_subdev_ops = { | 1021 | static struct v4l2_subdev_ops ov2640_subdev_ops = { |
@@ -1104,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = { | |||
1104 | static int ov2640_probe(struct i2c_client *client, | 1029 | static int ov2640_probe(struct i2c_client *client, |
1105 | const struct i2c_device_id *did) | 1030 | const struct i2c_device_id *did) |
1106 | { | 1031 | { |
1107 | struct ov2640_priv *priv; | 1032 | struct ov2640_priv *priv; |
1108 | struct soc_camera_device *icd = client->dev.platform_data; | 1033 | struct soc_camera_link *icl = soc_camera_i2c_to_link(client); |
1109 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | 1034 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
1110 | struct soc_camera_link *icl; | 1035 | int ret; |
1111 | int ret; | ||
1112 | |||
1113 | if (!icd) { | ||
1114 | dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n"); | ||
1115 | return -EINVAL; | ||
1116 | } | ||
1117 | 1036 | ||
1118 | icl = to_soc_camera_link(icd); | ||
1119 | if (!icl) { | 1037 | if (!icl) { |
1120 | dev_err(&adapter->dev, | 1038 | dev_err(&adapter->dev, |
1121 | "OV2640: Missing platform_data for driver\n"); | 1039 | "OV2640: Missing platform_data for driver\n"); |
@@ -1135,15 +1053,23 @@ static int ov2640_probe(struct i2c_client *client, | |||
1135 | return -ENOMEM; | 1053 | return -ENOMEM; |
1136 | } | 1054 | } |
1137 | 1055 | ||
1138 | priv->info = icl->priv; | ||
1139 | |||
1140 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); | 1056 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); |
1057 | v4l2_ctrl_handler_init(&priv->hdl, 2); | ||
1058 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, | ||
1059 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
1060 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, | ||
1061 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
1062 | priv->subdev.ctrl_handler = &priv->hdl; | ||
1063 | if (priv->hdl.error) { | ||
1064 | int err = priv->hdl.error; | ||
1141 | 1065 | ||
1142 | icd->ops = &ov2640_ops; | 1066 | kfree(priv); |
1067 | return err; | ||
1068 | } | ||
1143 | 1069 | ||
1144 | ret = ov2640_video_probe(icd, client); | 1070 | ret = ov2640_video_probe(client); |
1145 | if (ret) { | 1071 | if (ret) { |
1146 | icd->ops = NULL; | 1072 | v4l2_ctrl_handler_free(&priv->hdl); |
1147 | kfree(priv); | 1073 | kfree(priv); |
1148 | } else { | 1074 | } else { |
1149 | dev_info(&adapter->dev, "OV2640 Probed\n"); | 1075 | dev_info(&adapter->dev, "OV2640 Probed\n"); |
@@ -1155,9 +1081,9 @@ static int ov2640_probe(struct i2c_client *client, | |||
1155 | static int ov2640_remove(struct i2c_client *client) | 1081 | static int ov2640_remove(struct i2c_client *client) |
1156 | { | 1082 | { |
1157 | struct ov2640_priv *priv = to_ov2640(client); | 1083 | struct ov2640_priv *priv = to_ov2640(client); |
1158 | struct soc_camera_device *icd = client->dev.platform_data; | ||
1159 | 1084 | ||
1160 | icd->ops = NULL; | 1085 | v4l2_device_unregister_subdev(&priv->subdev); |
1086 | v4l2_ctrl_handler_free(&priv->hdl); | ||
1161 | kfree(priv); | 1087 | kfree(priv); |
1162 | return 0; | 1088 | return 0; |
1163 | } | 1089 | } |