Commit | Line | Data |
---|---|---|
cac47f18 AH |
1 | /* |
2 | * Samsung LSI S5C73M3 8M pixel camera driver | |
3 | * | |
4 | * Copyright (C) 2012, Samsung Electronics, Co., Ltd. | |
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | |
6 | * Andrzej Hajda <a.hajda@samsung.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | */ | |
17 | ||
18 | #include <linux/sizes.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/media.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/spi/spi.h> | |
25 | ||
26 | #include "s5c73m3.h" | |
27 | ||
28 | #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" | |
29 | ||
bce6744d SN |
30 | static const struct of_device_id s5c73m3_spi_ids[] = { |
31 | { .compatible = "samsung,s5c73m3" }, | |
32 | { } | |
33 | }; | |
5aa2ccfc | 34 | MODULE_DEVICE_TABLE(of, s5c73m3_spi_ids); |
bce6744d | 35 | |
cac47f18 AH |
36 | enum spi_direction { |
37 | SPI_DIR_RX, | |
38 | SPI_DIR_TX | |
39 | }; | |
40 | ||
41 | static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len, | |
42 | enum spi_direction dir) | |
43 | { | |
44 | struct spi_message msg; | |
45 | int r; | |
46 | struct spi_transfer xfer = { | |
47 | .len = len, | |
48 | }; | |
49 | ||
50 | if (dir == SPI_DIR_TX) | |
51 | xfer.tx_buf = addr; | |
52 | else | |
53 | xfer.rx_buf = addr; | |
54 | ||
55 | if (spi_dev == NULL) { | |
48205533 | 56 | pr_err("SPI device is uninitialized\n"); |
cac47f18 AH |
57 | return -ENODEV; |
58 | } | |
59 | ||
60 | spi_message_init(&msg); | |
61 | spi_message_add_tail(&xfer, &msg); | |
62 | ||
63 | r = spi_sync(spi_dev, &msg); | |
64 | if (r < 0) | |
65 | dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r); | |
66 | ||
67 | return r; | |
68 | } | |
69 | ||
70 | int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, | |
71 | const unsigned int len, const unsigned int tx_size) | |
72 | { | |
73 | struct spi_device *spi_dev = state->spi_dev; | |
74 | u32 count = len / tx_size; | |
75 | u32 extra = len % tx_size; | |
76 | unsigned int i, j = 0; | |
77 | u8 padding[32]; | |
78 | int r = 0; | |
79 | ||
80 | memset(padding, 0, sizeof(padding)); | |
81 | ||
8638a467 | 82 | for (i = 0; i < count; i++) { |
cac47f18 AH |
83 | r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX); |
84 | if (r < 0) | |
85 | return r; | |
86 | j += tx_size; | |
87 | } | |
88 | ||
89 | if (extra > 0) { | |
90 | r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX); | |
91 | if (r < 0) | |
92 | return r; | |
93 | } | |
94 | ||
95 | return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX); | |
96 | } | |
97 | ||
98 | int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, | |
99 | const unsigned int len, const unsigned int tx_size) | |
100 | { | |
101 | struct spi_device *spi_dev = state->spi_dev; | |
102 | u32 count = len / tx_size; | |
103 | u32 extra = len % tx_size; | |
104 | unsigned int i, j = 0; | |
105 | int r = 0; | |
106 | ||
8638a467 | 107 | for (i = 0; i < count; i++) { |
cac47f18 AH |
108 | r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX); |
109 | if (r < 0) | |
110 | return r; | |
111 | j += tx_size; | |
112 | } | |
113 | ||
114 | if (extra > 0) | |
115 | return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX); | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
c2668c08 | 120 | static int s5c73m3_spi_probe(struct spi_device *spi) |
cac47f18 AH |
121 | { |
122 | int r; | |
123 | struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, | |
124 | spidrv.driver); | |
125 | spi->bits_per_word = 32; | |
126 | ||
127 | r = spi_setup(spi); | |
128 | if (r < 0) { | |
129 | dev_err(&spi->dev, "spi_setup() failed\n"); | |
130 | return r; | |
131 | } | |
132 | ||
133 | mutex_lock(&state->lock); | |
134 | state->spi_dev = spi; | |
135 | mutex_unlock(&state->lock); | |
136 | ||
137 | v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n"); | |
138 | return 0; | |
139 | } | |
140 | ||
c2668c08 | 141 | static int s5c73m3_spi_remove(struct spi_device *spi) |
cac47f18 AH |
142 | { |
143 | return 0; | |
144 | } | |
145 | ||
146 | int s5c73m3_register_spi_driver(struct s5c73m3 *state) | |
147 | { | |
148 | struct spi_driver *spidrv = &state->spidrv; | |
149 | ||
c2668c08 | 150 | spidrv->remove = s5c73m3_spi_remove; |
cac47f18 AH |
151 | spidrv->probe = s5c73m3_spi_probe; |
152 | spidrv->driver.name = S5C73M3_SPI_DRV_NAME; | |
bce6744d | 153 | spidrv->driver.of_match_table = s5c73m3_spi_ids; |
cac47f18 AH |
154 | |
155 | return spi_register_driver(spidrv); | |
156 | } | |
157 | ||
158 | void s5c73m3_unregister_spi_driver(struct s5c73m3 *state) | |
159 | { | |
160 | spi_unregister_driver(&state->spidrv); | |
161 | } |