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 9ce2fa037b94..b5247cb64fde 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 | } |
