| .. | .. |
|---|
| 4 | 4 | * so it needs platform-specific support outside of the core. |
|---|
| 5 | 5 | * |
|---|
| 6 | 6 | * Copyright 2011 Jonathan Corbet corbet@lwn.net |
|---|
| 7 | + * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> |
|---|
| 7 | 8 | */ |
|---|
| 8 | 9 | #include <linux/kernel.h> |
|---|
| 9 | 10 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 21 | 22 | #include <linux/vmalloc.h> |
|---|
| 22 | 23 | #include <linux/io.h> |
|---|
| 23 | 24 | #include <linux/clk.h> |
|---|
| 25 | +#include <linux/clk-provider.h> |
|---|
| 24 | 26 | #include <linux/videodev2.h> |
|---|
| 27 | +#include <linux/pm_runtime.h> |
|---|
| 25 | 28 | #include <media/v4l2-device.h> |
|---|
| 26 | 29 | #include <media/v4l2-ioctl.h> |
|---|
| 27 | 30 | #include <media/v4l2-ctrls.h> |
|---|
| 28 | 31 | #include <media/v4l2-event.h> |
|---|
| 29 | | -#include <media/i2c/ov7670.h> |
|---|
| 30 | 32 | #include <media/videobuf2-vmalloc.h> |
|---|
| 31 | 33 | #include <media/videobuf2-dma-contig.h> |
|---|
| 32 | 34 | #include <media/videobuf2-dma-sg.h> |
|---|
| .. | .. |
|---|
| 93 | 95 | #define sensor_call(cam, o, f, args...) \ |
|---|
| 94 | 96 | v4l2_subdev_call(cam->sensor, o, f, ##args) |
|---|
| 95 | 97 | |
|---|
| 98 | +#define notifier_to_mcam(notifier) \ |
|---|
| 99 | + container_of(notifier, struct mcam_camera, notifier) |
|---|
| 100 | + |
|---|
| 96 | 101 | static struct mcam_format_struct { |
|---|
| 97 | | - __u8 *desc; |
|---|
| 98 | 102 | __u32 pixelformat; |
|---|
| 99 | 103 | int bpp; /* Bytes per pixel */ |
|---|
| 100 | 104 | bool planar; |
|---|
| 101 | 105 | u32 mbus_code; |
|---|
| 102 | 106 | } mcam_formats[] = { |
|---|
| 103 | 107 | { |
|---|
| 104 | | - .desc = "YUYV 4:2:2", |
|---|
| 105 | 108 | .pixelformat = V4L2_PIX_FMT_YUYV, |
|---|
| 106 | 109 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, |
|---|
| 107 | 110 | .bpp = 2, |
|---|
| 108 | 111 | .planar = false, |
|---|
| 109 | 112 | }, |
|---|
| 110 | 113 | { |
|---|
| 111 | | - .desc = "YVYU 4:2:2", |
|---|
| 112 | 114 | .pixelformat = V4L2_PIX_FMT_YVYU, |
|---|
| 113 | 115 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, |
|---|
| 114 | 116 | .bpp = 2, |
|---|
| 115 | 117 | .planar = false, |
|---|
| 116 | 118 | }, |
|---|
| 117 | 119 | { |
|---|
| 118 | | - .desc = "YUV 4:2:0 PLANAR", |
|---|
| 119 | 120 | .pixelformat = V4L2_PIX_FMT_YUV420, |
|---|
| 120 | 121 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, |
|---|
| 121 | 122 | .bpp = 1, |
|---|
| 122 | 123 | .planar = true, |
|---|
| 123 | 124 | }, |
|---|
| 124 | 125 | { |
|---|
| 125 | | - .desc = "YVU 4:2:0 PLANAR", |
|---|
| 126 | 126 | .pixelformat = V4L2_PIX_FMT_YVU420, |
|---|
| 127 | 127 | .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, |
|---|
| 128 | 128 | .bpp = 1, |
|---|
| 129 | 129 | .planar = true, |
|---|
| 130 | 130 | }, |
|---|
| 131 | 131 | { |
|---|
| 132 | | - .desc = "XRGB 444", |
|---|
| 133 | 132 | .pixelformat = V4L2_PIX_FMT_XRGB444, |
|---|
| 134 | 133 | .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, |
|---|
| 135 | 134 | .bpp = 2, |
|---|
| 136 | 135 | .planar = false, |
|---|
| 137 | 136 | }, |
|---|
| 138 | 137 | { |
|---|
| 139 | | - .desc = "RGB 565", |
|---|
| 140 | 138 | .pixelformat = V4L2_PIX_FMT_RGB565, |
|---|
| 141 | 139 | .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, |
|---|
| 142 | 140 | .bpp = 2, |
|---|
| 143 | 141 | .planar = false, |
|---|
| 144 | 142 | }, |
|---|
| 145 | 143 | { |
|---|
| 146 | | - .desc = "Raw RGB Bayer", |
|---|
| 147 | 144 | .pixelformat = V4L2_PIX_FMT_SBGGR8, |
|---|
| 148 | 145 | .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, |
|---|
| 149 | 146 | .bpp = 1, |
|---|
| .. | .. |
|---|
| 281 | 278 | static void mcam_enable_mipi(struct mcam_camera *mcam) |
|---|
| 282 | 279 | { |
|---|
| 283 | 280 | /* Using MIPI mode and enable MIPI */ |
|---|
| 281 | + if (mcam->calc_dphy) |
|---|
| 282 | + mcam->calc_dphy(mcam); |
|---|
| 284 | 283 | cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n", |
|---|
| 285 | 284 | mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]); |
|---|
| 286 | 285 | mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]); |
|---|
| .. | .. |
|---|
| 300 | 299 | */ |
|---|
| 301 | 300 | mcam_reg_write(mcam, REG_CSI2_CTRL0, |
|---|
| 302 | 301 | CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane)); |
|---|
| 303 | | - mcam_reg_write(mcam, REG_CLKCTRL, |
|---|
| 304 | | - (mcam->mclk_src << 29) | mcam->mclk_div); |
|---|
| 305 | | - |
|---|
| 306 | 302 | mcam->mipi_enabled = true; |
|---|
| 307 | 303 | } |
|---|
| 308 | 304 | } |
|---|
| .. | .. |
|---|
| 393 | 389 | dma_free_coherent(cam->dev, cam->dma_buf_size, |
|---|
| 394 | 390 | cam->dma_bufs[0], cam->dma_handles[0]); |
|---|
| 395 | 391 | cam->nbufs = 0; |
|---|
| 396 | | - /* fall-through */ |
|---|
| 392 | + fallthrough; |
|---|
| 397 | 393 | case 0: |
|---|
| 398 | 394 | cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); |
|---|
| 399 | 395 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 443 | 439 | /* |
|---|
| 444 | 440 | * Copy data out to user space in the vmalloc case |
|---|
| 445 | 441 | */ |
|---|
| 446 | | -static void mcam_frame_tasklet(unsigned long data) |
|---|
| 442 | +static void mcam_frame_tasklet(struct tasklet_struct *t) |
|---|
| 447 | 443 | { |
|---|
| 448 | | - struct mcam_camera *cam = (struct mcam_camera *) data; |
|---|
| 444 | + struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet); |
|---|
| 449 | 445 | int i; |
|---|
| 450 | 446 | unsigned long flags; |
|---|
| 451 | 447 | struct mcam_vb_buffer *buf; |
|---|
| .. | .. |
|---|
| 792 | 788 | * Make sure it knows we want to use hsync/vsync. |
|---|
| 793 | 789 | */ |
|---|
| 794 | 790 | mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); |
|---|
| 795 | | - /* |
|---|
| 796 | | - * This field controls the generation of EOF(DVP only) |
|---|
| 797 | | - */ |
|---|
| 798 | | - if (cam->bus_type != V4L2_MBUS_CSI2) |
|---|
| 799 | | - mcam_reg_set_bit(cam, REG_CTRL0, |
|---|
| 800 | | - C0_EOF_VSYNC | C0_VEDGE_CTRL); |
|---|
| 801 | 791 | } |
|---|
| 802 | 792 | |
|---|
| 803 | 793 | |
|---|
| .. | .. |
|---|
| 832 | 822 | { |
|---|
| 833 | 823 | mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); |
|---|
| 834 | 824 | } |
|---|
| 835 | | - |
|---|
| 836 | | - |
|---|
| 837 | | - |
|---|
| 838 | | -static void mcam_ctlr_init(struct mcam_camera *cam) |
|---|
| 839 | | -{ |
|---|
| 840 | | - unsigned long flags; |
|---|
| 841 | | - |
|---|
| 842 | | - spin_lock_irqsave(&cam->dev_lock, flags); |
|---|
| 843 | | - /* |
|---|
| 844 | | - * Make sure it's not powered down. |
|---|
| 845 | | - */ |
|---|
| 846 | | - mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); |
|---|
| 847 | | - /* |
|---|
| 848 | | - * Turn off the enable bit. It sure should be off anyway, |
|---|
| 849 | | - * but it's good to be sure. |
|---|
| 850 | | - */ |
|---|
| 851 | | - mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); |
|---|
| 852 | | - /* |
|---|
| 853 | | - * Clock the sensor appropriately. Controller clock should |
|---|
| 854 | | - * be 48MHz, sensor "typical" value is half that. |
|---|
| 855 | | - */ |
|---|
| 856 | | - mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); |
|---|
| 857 | | - spin_unlock_irqrestore(&cam->dev_lock, flags); |
|---|
| 858 | | -} |
|---|
| 859 | | - |
|---|
| 860 | 825 | |
|---|
| 861 | 826 | /* |
|---|
| 862 | 827 | * Stop the controller, and don't return until we're really sure that no |
|---|
| .. | .. |
|---|
| 901 | 866 | int ret; |
|---|
| 902 | 867 | |
|---|
| 903 | 868 | spin_lock_irqsave(&cam->dev_lock, flags); |
|---|
| 904 | | - ret = cam->plat_power_up(cam); |
|---|
| 905 | | - if (ret) { |
|---|
| 906 | | - spin_unlock_irqrestore(&cam->dev_lock, flags); |
|---|
| 907 | | - return ret; |
|---|
| 869 | + if (cam->plat_power_up) { |
|---|
| 870 | + ret = cam->plat_power_up(cam); |
|---|
| 871 | + if (ret) { |
|---|
| 872 | + spin_unlock_irqrestore(&cam->dev_lock, flags); |
|---|
| 873 | + return ret; |
|---|
| 874 | + } |
|---|
| 908 | 875 | } |
|---|
| 909 | 876 | mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); |
|---|
| 910 | 877 | spin_unlock_irqrestore(&cam->dev_lock, flags); |
|---|
| 911 | | - msleep(5); /* Just to be sure */ |
|---|
| 912 | 878 | return 0; |
|---|
| 913 | 879 | } |
|---|
| 914 | 880 | |
|---|
| .. | .. |
|---|
| 923 | 889 | * power down routine. |
|---|
| 924 | 890 | */ |
|---|
| 925 | 891 | mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); |
|---|
| 926 | | - cam->plat_power_down(cam); |
|---|
| 892 | + if (cam->plat_power_down) |
|---|
| 893 | + cam->plat_power_down(cam); |
|---|
| 927 | 894 | spin_unlock_irqrestore(&cam->dev_lock, flags); |
|---|
| 928 | 895 | } |
|---|
| 896 | + |
|---|
| 897 | +/* ---------------------------------------------------------------------- */ |
|---|
| 898 | +/* |
|---|
| 899 | + * Master sensor clock. |
|---|
| 900 | + */ |
|---|
| 901 | +static int mclk_prepare(struct clk_hw *hw) |
|---|
| 902 | +{ |
|---|
| 903 | + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); |
|---|
| 904 | + |
|---|
| 905 | + clk_prepare(cam->clk[0]); |
|---|
| 906 | + return 0; |
|---|
| 907 | +} |
|---|
| 908 | + |
|---|
| 909 | +static void mclk_unprepare(struct clk_hw *hw) |
|---|
| 910 | +{ |
|---|
| 911 | + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); |
|---|
| 912 | + |
|---|
| 913 | + clk_unprepare(cam->clk[0]); |
|---|
| 914 | +} |
|---|
| 915 | + |
|---|
| 916 | +static int mclk_enable(struct clk_hw *hw) |
|---|
| 917 | +{ |
|---|
| 918 | + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); |
|---|
| 919 | + int mclk_src; |
|---|
| 920 | + int mclk_div; |
|---|
| 921 | + int ret; |
|---|
| 922 | + |
|---|
| 923 | + /* |
|---|
| 924 | + * Clock the sensor appropriately. Controller clock should |
|---|
| 925 | + * be 48MHz, sensor "typical" value is half that. |
|---|
| 926 | + */ |
|---|
| 927 | + if (cam->bus_type == V4L2_MBUS_CSI2_DPHY) { |
|---|
| 928 | + mclk_src = cam->mclk_src; |
|---|
| 929 | + mclk_div = cam->mclk_div; |
|---|
| 930 | + } else { |
|---|
| 931 | + mclk_src = 3; |
|---|
| 932 | + mclk_div = 2; |
|---|
| 933 | + } |
|---|
| 934 | + |
|---|
| 935 | + ret = pm_runtime_resume_and_get(cam->dev); |
|---|
| 936 | + if (ret < 0) |
|---|
| 937 | + return ret; |
|---|
| 938 | + clk_enable(cam->clk[0]); |
|---|
| 939 | + mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div); |
|---|
| 940 | + mcam_ctlr_power_up(cam); |
|---|
| 941 | + |
|---|
| 942 | + return 0; |
|---|
| 943 | +} |
|---|
| 944 | + |
|---|
| 945 | +static void mclk_disable(struct clk_hw *hw) |
|---|
| 946 | +{ |
|---|
| 947 | + struct mcam_camera *cam = container_of(hw, struct mcam_camera, mclk_hw); |
|---|
| 948 | + |
|---|
| 949 | + mcam_ctlr_power_down(cam); |
|---|
| 950 | + clk_disable(cam->clk[0]); |
|---|
| 951 | + pm_runtime_put(cam->dev); |
|---|
| 952 | +} |
|---|
| 953 | + |
|---|
| 954 | +static unsigned long mclk_recalc_rate(struct clk_hw *hw, |
|---|
| 955 | + unsigned long parent_rate) |
|---|
| 956 | +{ |
|---|
| 957 | + return 48000000; |
|---|
| 958 | +} |
|---|
| 959 | + |
|---|
| 960 | +static const struct clk_ops mclk_ops = { |
|---|
| 961 | + .prepare = mclk_prepare, |
|---|
| 962 | + .unprepare = mclk_unprepare, |
|---|
| 963 | + .enable = mclk_enable, |
|---|
| 964 | + .disable = mclk_disable, |
|---|
| 965 | + .recalc_rate = mclk_recalc_rate, |
|---|
| 966 | +}; |
|---|
| 929 | 967 | |
|---|
| 930 | 968 | /* -------------------------------------------------------------------- */ |
|---|
| 931 | 969 | /* |
|---|
| .. | .. |
|---|
| 951 | 989 | ret = __mcam_cam_reset(cam); |
|---|
| 952 | 990 | /* Get/set parameters? */ |
|---|
| 953 | 991 | cam->state = S_IDLE; |
|---|
| 954 | | - mcam_ctlr_power_down(cam); |
|---|
| 955 | 992 | return ret; |
|---|
| 956 | 993 | } |
|---|
| 957 | 994 | |
|---|
| .. | .. |
|---|
| 1017 | 1054 | spin_lock_irqsave(&cam->dev_lock, flags); |
|---|
| 1018 | 1055 | clear_bit(CF_DMA_ACTIVE, &cam->flags); |
|---|
| 1019 | 1056 | mcam_reset_buffers(cam); |
|---|
| 1020 | | - /* |
|---|
| 1021 | | - * Update CSI2_DPHY value |
|---|
| 1022 | | - */ |
|---|
| 1023 | | - if (cam->calc_dphy) |
|---|
| 1024 | | - cam->calc_dphy(cam); |
|---|
| 1025 | | - cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n", |
|---|
| 1026 | | - cam->dphy[0], cam->dphy[1], cam->dphy[2]); |
|---|
| 1027 | | - if (cam->bus_type == V4L2_MBUS_CSI2) |
|---|
| 1057 | + if (cam->bus_type == V4L2_MBUS_CSI2_DPHY) |
|---|
| 1028 | 1058 | mcam_enable_mipi(cam); |
|---|
| 1029 | 1059 | else |
|---|
| 1030 | 1060 | mcam_disable_mipi(cam); |
|---|
| .. | .. |
|---|
| 1161 | 1191 | return; |
|---|
| 1162 | 1192 | mcam_ctlr_stop_dma(cam); |
|---|
| 1163 | 1193 | /* |
|---|
| 1164 | | - * Reset the CCIC PHY after stopping streaming, |
|---|
| 1165 | | - * otherwise, the CCIC may be unstable. |
|---|
| 1166 | | - */ |
|---|
| 1167 | | - if (cam->ctlr_reset) |
|---|
| 1168 | | - cam->ctlr_reset(cam); |
|---|
| 1169 | | - /* |
|---|
| 1170 | 1194 | * VB2 reclaims the buffers, so we need to forget |
|---|
| 1171 | 1195 | * about them. |
|---|
| 1172 | 1196 | */ |
|---|
| .. | .. |
|---|
| 1281 | 1305 | break; |
|---|
| 1282 | 1306 | case B_vmalloc: |
|---|
| 1283 | 1307 | #ifdef MCAM_MODE_VMALLOC |
|---|
| 1284 | | - tasklet_init(&cam->s_tasklet, mcam_frame_tasklet, |
|---|
| 1285 | | - (unsigned long) cam); |
|---|
| 1308 | + tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet); |
|---|
| 1286 | 1309 | vq->ops = &mcam_vb2_ops; |
|---|
| 1287 | 1310 | vq->mem_ops = &vb2_vmalloc_memops; |
|---|
| 1288 | 1311 | cam->dma_setup = mcam_ctlr_dma_vmalloc; |
|---|
| .. | .. |
|---|
| 1304 | 1327 | { |
|---|
| 1305 | 1328 | struct mcam_camera *cam = video_drvdata(file); |
|---|
| 1306 | 1329 | |
|---|
| 1307 | | - strcpy(cap->driver, "marvell_ccic"); |
|---|
| 1308 | | - strcpy(cap->card, "marvell_ccic"); |
|---|
| 1309 | | - strlcpy(cap->bus_info, cam->bus_info, sizeof(cap->bus_info)); |
|---|
| 1310 | | - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | |
|---|
| 1311 | | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; |
|---|
| 1312 | | - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; |
|---|
| 1330 | + strscpy(cap->driver, "marvell_ccic", sizeof(cap->driver)); |
|---|
| 1331 | + strscpy(cap->card, "marvell_ccic", sizeof(cap->card)); |
|---|
| 1332 | + strscpy(cap->bus_info, cam->bus_info, sizeof(cap->bus_info)); |
|---|
| 1313 | 1333 | return 0; |
|---|
| 1314 | 1334 | } |
|---|
| 1315 | 1335 | |
|---|
| .. | .. |
|---|
| 1319 | 1339 | { |
|---|
| 1320 | 1340 | if (fmt->index >= N_MCAM_FMTS) |
|---|
| 1321 | 1341 | return -EINVAL; |
|---|
| 1322 | | - strlcpy(fmt->description, mcam_formats[fmt->index].desc, |
|---|
| 1323 | | - sizeof(fmt->description)); |
|---|
| 1324 | 1342 | fmt->pixelformat = mcam_formats[fmt->index].pixelformat; |
|---|
| 1325 | 1343 | return 0; |
|---|
| 1326 | 1344 | } |
|---|
| .. | .. |
|---|
| 1422 | 1440 | return -EINVAL; |
|---|
| 1423 | 1441 | |
|---|
| 1424 | 1442 | input->type = V4L2_INPUT_TYPE_CAMERA; |
|---|
| 1425 | | - strcpy(input->name, "Camera"); |
|---|
| 1443 | + strscpy(input->name, "Camera", sizeof(input->name)); |
|---|
| 1426 | 1444 | return 0; |
|---|
| 1427 | 1445 | } |
|---|
| 1428 | 1446 | |
|---|
| .. | .. |
|---|
| 1593 | 1611 | if (ret) |
|---|
| 1594 | 1612 | goto out; |
|---|
| 1595 | 1613 | if (v4l2_fh_is_singular_file(filp)) { |
|---|
| 1596 | | - ret = mcam_ctlr_power_up(cam); |
|---|
| 1614 | + ret = sensor_call(cam, core, s_power, 1); |
|---|
| 1597 | 1615 | if (ret) |
|---|
| 1616 | + goto out; |
|---|
| 1617 | + ret = pm_runtime_resume_and_get(cam->dev); |
|---|
| 1618 | + if (ret < 0) |
|---|
| 1598 | 1619 | goto out; |
|---|
| 1599 | 1620 | __mcam_cam_reset(cam); |
|---|
| 1600 | 1621 | mcam_set_config_needed(cam, 1); |
|---|
| .. | .. |
|---|
| 1617 | 1638 | _vb2_fop_release(filp, NULL); |
|---|
| 1618 | 1639 | if (last_open) { |
|---|
| 1619 | 1640 | mcam_disable_mipi(cam); |
|---|
| 1620 | | - mcam_ctlr_power_down(cam); |
|---|
| 1641 | + sensor_call(cam, core, s_power, 0); |
|---|
| 1642 | + pm_runtime_put(cam->dev); |
|---|
| 1621 | 1643 | if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) |
|---|
| 1622 | 1644 | mcam_free_dma_bufs(cam); |
|---|
| 1623 | 1645 | } |
|---|
| .. | .. |
|---|
| 1646 | 1668 | .fops = &mcam_v4l_fops, |
|---|
| 1647 | 1669 | .ioctl_ops = &mcam_v4l_ioctl_ops, |
|---|
| 1648 | 1670 | .release = video_device_release_empty, |
|---|
| 1671 | + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | |
|---|
| 1672 | + V4L2_CAP_STREAMING, |
|---|
| 1649 | 1673 | }; |
|---|
| 1650 | 1674 | |
|---|
| 1651 | 1675 | /* ---------------------------------------------------------------------- */ |
|---|
| .. | .. |
|---|
| 1727 | 1751 | /* |
|---|
| 1728 | 1752 | * Registration and such. |
|---|
| 1729 | 1753 | */ |
|---|
| 1730 | | -static struct ov7670_config sensor_cfg = { |
|---|
| 1731 | | - /* |
|---|
| 1732 | | - * Exclude QCIF mode, because it only captures a tiny portion |
|---|
| 1733 | | - * of the sensor FOV |
|---|
| 1734 | | - */ |
|---|
| 1735 | | - .min_width = 320, |
|---|
| 1736 | | - .min_height = 240, |
|---|
| 1737 | | -}; |
|---|
| 1738 | 1754 | |
|---|
| 1755 | +static int mccic_notify_bound(struct v4l2_async_notifier *notifier, |
|---|
| 1756 | + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) |
|---|
| 1757 | +{ |
|---|
| 1758 | + struct mcam_camera *cam = notifier_to_mcam(notifier); |
|---|
| 1759 | + int ret; |
|---|
| 1760 | + |
|---|
| 1761 | + mutex_lock(&cam->s_mutex); |
|---|
| 1762 | + if (cam->sensor) { |
|---|
| 1763 | + cam_err(cam, "sensor already bound\n"); |
|---|
| 1764 | + ret = -EBUSY; |
|---|
| 1765 | + goto out; |
|---|
| 1766 | + } |
|---|
| 1767 | + |
|---|
| 1768 | + v4l2_set_subdev_hostdata(subdev, cam); |
|---|
| 1769 | + cam->sensor = subdev; |
|---|
| 1770 | + |
|---|
| 1771 | + ret = mcam_cam_init(cam); |
|---|
| 1772 | + if (ret) { |
|---|
| 1773 | + cam->sensor = NULL; |
|---|
| 1774 | + goto out; |
|---|
| 1775 | + } |
|---|
| 1776 | + |
|---|
| 1777 | + ret = mcam_setup_vb2(cam); |
|---|
| 1778 | + if (ret) { |
|---|
| 1779 | + cam->sensor = NULL; |
|---|
| 1780 | + goto out; |
|---|
| 1781 | + } |
|---|
| 1782 | + |
|---|
| 1783 | + cam->vdev = mcam_v4l_template; |
|---|
| 1784 | + cam->vdev.v4l2_dev = &cam->v4l2_dev; |
|---|
| 1785 | + cam->vdev.lock = &cam->s_mutex; |
|---|
| 1786 | + cam->vdev.queue = &cam->vb_queue; |
|---|
| 1787 | + video_set_drvdata(&cam->vdev, cam); |
|---|
| 1788 | + ret = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); |
|---|
| 1789 | + if (ret) { |
|---|
| 1790 | + cam->sensor = NULL; |
|---|
| 1791 | + goto out; |
|---|
| 1792 | + } |
|---|
| 1793 | + |
|---|
| 1794 | + cam_dbg(cam, "sensor %s bound\n", subdev->name); |
|---|
| 1795 | +out: |
|---|
| 1796 | + mutex_unlock(&cam->s_mutex); |
|---|
| 1797 | + return ret; |
|---|
| 1798 | +} |
|---|
| 1799 | + |
|---|
| 1800 | +static void mccic_notify_unbind(struct v4l2_async_notifier *notifier, |
|---|
| 1801 | + struct v4l2_subdev *subdev, struct v4l2_async_subdev *asd) |
|---|
| 1802 | +{ |
|---|
| 1803 | + struct mcam_camera *cam = notifier_to_mcam(notifier); |
|---|
| 1804 | + |
|---|
| 1805 | + mutex_lock(&cam->s_mutex); |
|---|
| 1806 | + if (cam->sensor != subdev) { |
|---|
| 1807 | + cam_err(cam, "sensor %s not bound\n", subdev->name); |
|---|
| 1808 | + goto out; |
|---|
| 1809 | + } |
|---|
| 1810 | + |
|---|
| 1811 | + video_unregister_device(&cam->vdev); |
|---|
| 1812 | + cam->sensor = NULL; |
|---|
| 1813 | + cam_dbg(cam, "sensor %s unbound\n", subdev->name); |
|---|
| 1814 | + |
|---|
| 1815 | +out: |
|---|
| 1816 | + mutex_unlock(&cam->s_mutex); |
|---|
| 1817 | +} |
|---|
| 1818 | + |
|---|
| 1819 | +static int mccic_notify_complete(struct v4l2_async_notifier *notifier) |
|---|
| 1820 | +{ |
|---|
| 1821 | + struct mcam_camera *cam = notifier_to_mcam(notifier); |
|---|
| 1822 | + int ret; |
|---|
| 1823 | + |
|---|
| 1824 | + /* |
|---|
| 1825 | + * Get the v4l2 setup done. |
|---|
| 1826 | + */ |
|---|
| 1827 | + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); |
|---|
| 1828 | + if (!ret) |
|---|
| 1829 | + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; |
|---|
| 1830 | + |
|---|
| 1831 | + return ret; |
|---|
| 1832 | +} |
|---|
| 1833 | + |
|---|
| 1834 | +static const struct v4l2_async_notifier_operations mccic_notify_ops = { |
|---|
| 1835 | + .bound = mccic_notify_bound, |
|---|
| 1836 | + .unbind = mccic_notify_unbind, |
|---|
| 1837 | + .complete = mccic_notify_complete, |
|---|
| 1838 | +}; |
|---|
| 1739 | 1839 | |
|---|
| 1740 | 1840 | int mccic_register(struct mcam_camera *cam) |
|---|
| 1741 | 1841 | { |
|---|
| 1742 | | - struct i2c_board_info ov7670_info = { |
|---|
| 1743 | | - .type = "ov7670", |
|---|
| 1744 | | - .addr = 0x42 >> 1, |
|---|
| 1745 | | - .platform_data = &sensor_cfg, |
|---|
| 1746 | | - }; |
|---|
| 1842 | + struct clk_init_data mclk_init = { }; |
|---|
| 1747 | 1843 | int ret; |
|---|
| 1748 | 1844 | |
|---|
| 1749 | 1845 | /* |
|---|
| .. | .. |
|---|
| 1756 | 1852 | printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, attempting vmalloc mode instead\n"); |
|---|
| 1757 | 1853 | cam->buffer_mode = B_vmalloc; |
|---|
| 1758 | 1854 | } |
|---|
| 1855 | + |
|---|
| 1759 | 1856 | if (!mcam_buffer_mode_supported(cam->buffer_mode)) { |
|---|
| 1760 | 1857 | printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n", |
|---|
| 1761 | 1858 | cam->buffer_mode); |
|---|
| 1762 | | - return -EINVAL; |
|---|
| 1859 | + ret = -EINVAL; |
|---|
| 1860 | + goto out; |
|---|
| 1763 | 1861 | } |
|---|
| 1862 | + |
|---|
| 1764 | 1863 | /* |
|---|
| 1765 | 1864 | * Register with V4L |
|---|
| 1766 | 1865 | */ |
|---|
| 1767 | 1866 | ret = v4l2_device_register(cam->dev, &cam->v4l2_dev); |
|---|
| 1768 | 1867 | if (ret) |
|---|
| 1769 | | - return ret; |
|---|
| 1868 | + goto out; |
|---|
| 1770 | 1869 | |
|---|
| 1771 | 1870 | mutex_init(&cam->s_mutex); |
|---|
| 1772 | 1871 | cam->state = S_NOTREADY; |
|---|
| 1773 | 1872 | mcam_set_config_needed(cam, 1); |
|---|
| 1774 | 1873 | cam->pix_format = mcam_def_pix_format; |
|---|
| 1775 | 1874 | cam->mbus_code = mcam_def_mbus_code; |
|---|
| 1776 | | - mcam_ctlr_init(cam); |
|---|
| 1777 | 1875 | |
|---|
| 1778 | 1876 | /* |
|---|
| 1779 | | - * Get the v4l2 setup done. |
|---|
| 1877 | + * Register sensor notifier. |
|---|
| 1780 | 1878 | */ |
|---|
| 1781 | | - ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); |
|---|
| 1782 | | - if (ret) |
|---|
| 1783 | | - goto out_unregister; |
|---|
| 1784 | | - cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; |
|---|
| 1785 | | - |
|---|
| 1786 | | - /* |
|---|
| 1787 | | - * Try to find the sensor. |
|---|
| 1788 | | - */ |
|---|
| 1789 | | - sensor_cfg.clock_speed = cam->clock_speed; |
|---|
| 1790 | | - sensor_cfg.use_smbus = cam->use_smbus; |
|---|
| 1791 | | - cam->sensor_addr = ov7670_info.addr; |
|---|
| 1792 | | - cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, |
|---|
| 1793 | | - cam->i2c_adapter, &ov7670_info, NULL); |
|---|
| 1794 | | - if (cam->sensor == NULL) { |
|---|
| 1795 | | - ret = -ENODEV; |
|---|
| 1796 | | - goto out_unregister; |
|---|
| 1879 | + v4l2_async_notifier_init(&cam->notifier); |
|---|
| 1880 | + ret = v4l2_async_notifier_add_subdev(&cam->notifier, &cam->asd); |
|---|
| 1881 | + if (ret) { |
|---|
| 1882 | + cam_warn(cam, "failed to add subdev to a notifier"); |
|---|
| 1883 | + goto out; |
|---|
| 1797 | 1884 | } |
|---|
| 1798 | 1885 | |
|---|
| 1799 | | - ret = mcam_cam_init(cam); |
|---|
| 1800 | | - if (ret) |
|---|
| 1801 | | - goto out_unregister; |
|---|
| 1886 | + cam->notifier.ops = &mccic_notify_ops; |
|---|
| 1887 | + ret = v4l2_async_notifier_register(&cam->v4l2_dev, &cam->notifier); |
|---|
| 1888 | + if (ret < 0) { |
|---|
| 1889 | + cam_warn(cam, "failed to register a sensor notifier"); |
|---|
| 1890 | + goto out; |
|---|
| 1891 | + } |
|---|
| 1802 | 1892 | |
|---|
| 1803 | | - ret = mcam_setup_vb2(cam); |
|---|
| 1804 | | - if (ret) |
|---|
| 1805 | | - goto out_unregister; |
|---|
| 1893 | + /* |
|---|
| 1894 | + * Register sensor master clock. |
|---|
| 1895 | + */ |
|---|
| 1896 | + mclk_init.parent_names = NULL; |
|---|
| 1897 | + mclk_init.num_parents = 0; |
|---|
| 1898 | + mclk_init.ops = &mclk_ops; |
|---|
| 1899 | + mclk_init.name = "mclk"; |
|---|
| 1806 | 1900 | |
|---|
| 1807 | | - mutex_lock(&cam->s_mutex); |
|---|
| 1808 | | - cam->vdev = mcam_v4l_template; |
|---|
| 1809 | | - cam->vdev.v4l2_dev = &cam->v4l2_dev; |
|---|
| 1810 | | - cam->vdev.lock = &cam->s_mutex; |
|---|
| 1811 | | - cam->vdev.queue = &cam->vb_queue; |
|---|
| 1812 | | - video_set_drvdata(&cam->vdev, cam); |
|---|
| 1813 | | - ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); |
|---|
| 1814 | | - if (ret) { |
|---|
| 1815 | | - mutex_unlock(&cam->s_mutex); |
|---|
| 1816 | | - goto out_unregister; |
|---|
| 1901 | + of_property_read_string(cam->dev->of_node, "clock-output-names", |
|---|
| 1902 | + &mclk_init.name); |
|---|
| 1903 | + |
|---|
| 1904 | + cam->mclk_hw.init = &mclk_init; |
|---|
| 1905 | + |
|---|
| 1906 | + cam->mclk = devm_clk_register(cam->dev, &cam->mclk_hw); |
|---|
| 1907 | + if (IS_ERR(cam->mclk)) { |
|---|
| 1908 | + ret = PTR_ERR(cam->mclk); |
|---|
| 1909 | + dev_err(cam->dev, "can't register clock\n"); |
|---|
| 1910 | + goto out; |
|---|
| 1817 | 1911 | } |
|---|
| 1818 | 1912 | |
|---|
| 1819 | 1913 | /* |
|---|
| .. | .. |
|---|
| 1824 | 1918 | cam_warn(cam, "Unable to alloc DMA buffers at load will try again later."); |
|---|
| 1825 | 1919 | } |
|---|
| 1826 | 1920 | |
|---|
| 1827 | | - mutex_unlock(&cam->s_mutex); |
|---|
| 1828 | 1921 | return 0; |
|---|
| 1829 | 1922 | |
|---|
| 1830 | | -out_unregister: |
|---|
| 1831 | | - v4l2_ctrl_handler_free(&cam->ctrl_handler); |
|---|
| 1923 | +out: |
|---|
| 1924 | + v4l2_async_notifier_unregister(&cam->notifier); |
|---|
| 1832 | 1925 | v4l2_device_unregister(&cam->v4l2_dev); |
|---|
| 1926 | + v4l2_async_notifier_cleanup(&cam->notifier); |
|---|
| 1833 | 1927 | return ret; |
|---|
| 1834 | 1928 | } |
|---|
| 1835 | 1929 | EXPORT_SYMBOL_GPL(mccic_register); |
|---|
| .. | .. |
|---|
| 1844 | 1938 | */ |
|---|
| 1845 | 1939 | if (!list_empty(&cam->vdev.fh_list)) { |
|---|
| 1846 | 1940 | cam_warn(cam, "Removing a device with users!\n"); |
|---|
| 1847 | | - mcam_ctlr_power_down(cam); |
|---|
| 1941 | + sensor_call(cam, core, s_power, 0); |
|---|
| 1848 | 1942 | } |
|---|
| 1849 | 1943 | if (cam->buffer_mode == B_vmalloc) |
|---|
| 1850 | 1944 | mcam_free_dma_bufs(cam); |
|---|
| 1851 | | - video_unregister_device(&cam->vdev); |
|---|
| 1852 | 1945 | v4l2_ctrl_handler_free(&cam->ctrl_handler); |
|---|
| 1946 | + v4l2_async_notifier_unregister(&cam->notifier); |
|---|
| 1853 | 1947 | v4l2_device_unregister(&cam->v4l2_dev); |
|---|
| 1948 | + v4l2_async_notifier_cleanup(&cam->notifier); |
|---|
| 1854 | 1949 | } |
|---|
| 1855 | 1950 | EXPORT_SYMBOL_GPL(mccic_shutdown); |
|---|
| 1856 | 1951 | |
|---|
| 1857 | 1952 | /* |
|---|
| 1858 | 1953 | * Power management |
|---|
| 1859 | 1954 | */ |
|---|
| 1860 | | -#ifdef CONFIG_PM |
|---|
| 1861 | | - |
|---|
| 1862 | 1955 | void mccic_suspend(struct mcam_camera *cam) |
|---|
| 1863 | 1956 | { |
|---|
| 1864 | 1957 | mutex_lock(&cam->s_mutex); |
|---|
| .. | .. |
|---|
| 1866 | 1959 | enum mcam_state cstate = cam->state; |
|---|
| 1867 | 1960 | |
|---|
| 1868 | 1961 | mcam_ctlr_stop_dma(cam); |
|---|
| 1869 | | - mcam_ctlr_power_down(cam); |
|---|
| 1962 | + sensor_call(cam, core, s_power, 0); |
|---|
| 1870 | 1963 | cam->state = cstate; |
|---|
| 1871 | 1964 | } |
|---|
| 1872 | 1965 | mutex_unlock(&cam->s_mutex); |
|---|
| .. | .. |
|---|
| 1879 | 1972 | |
|---|
| 1880 | 1973 | mutex_lock(&cam->s_mutex); |
|---|
| 1881 | 1974 | if (!list_empty(&cam->vdev.fh_list)) { |
|---|
| 1882 | | - ret = mcam_ctlr_power_up(cam); |
|---|
| 1975 | + ret = sensor_call(cam, core, s_power, 1); |
|---|
| 1883 | 1976 | if (ret) { |
|---|
| 1884 | 1977 | mutex_unlock(&cam->s_mutex); |
|---|
| 1885 | 1978 | return ret; |
|---|
| 1886 | 1979 | } |
|---|
| 1887 | 1980 | __mcam_cam_reset(cam); |
|---|
| 1888 | 1981 | } else { |
|---|
| 1889 | | - mcam_ctlr_power_down(cam); |
|---|
| 1982 | + sensor_call(cam, core, s_power, 0); |
|---|
| 1890 | 1983 | } |
|---|
| 1891 | 1984 | mutex_unlock(&cam->s_mutex); |
|---|
| 1892 | 1985 | |
|---|
| .. | .. |
|---|
| 1903 | 1996 | return ret; |
|---|
| 1904 | 1997 | } |
|---|
| 1905 | 1998 | EXPORT_SYMBOL_GPL(mccic_resume); |
|---|
| 1906 | | -#endif /* CONFIG_PM */ |
|---|
| 1907 | 1999 | |
|---|
| 1908 | 2000 | MODULE_LICENSE("GPL v2"); |
|---|
| 1909 | 2001 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); |
|---|